[
  {
    "path": ".editorconfig",
    "content": "[*]\ncharset = utf-8\nindent_size = 4\nindent_style = space\ninsert_final_newline = true\ntab_width = 4\n\n[*.java]\nij_java_class_count_to_use_import_on_demand = 999999\nij_java_names_count_to_use_import_on_demand = 999999\nij_java_imports_layout = *, |, $*\nij_java_generate_final_locals = true\nij_java_generate_final_parameters = true\n\n[{*.json,*.yml}]\nindent_size = 2"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "github: kennytv\npatreon: kennytv\ncustom: ['https://florianreuth.de/donate']\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.yml",
    "content": "name: Bug Report\ndescription: Report a bug or console error\nlabels: [unconfirmed]\n\nbody:\n  - type: markdown\n    attributes:\n      value: \"**Before reporting a bug, please see if using master/dev builds from https://ci.viaversion.com/ fixes your issue.**\"\n\n  - type: input\n    attributes:\n      label: \"'/viaversion dump' Output\"\n      description: |\n        Run `/viaversion dump` in the console or in the chat, then copy and paste the given link here.\n      placeholder: |\n        https://dump.viaversion.com/...\n    validations:\n      required: true\n\n  - type: textarea\n    attributes:\n      label: Server/Client Error\n      description: |\n        If you encounter warnings/errors in your console, **paste them with https://mclo.gs/ and put the paste link here**.\n        If the error is small/less than 10 lines, you may put it directly into this field.\n        **Important**: If you are kicked for `Network Protocol Error` or an encoder/decoder exception, please click the `Open Report Directory` button on your client and paste the newest disconnect file contents.\n      value: |\n        ```\n        Put the mclo.gs link or text here.\n        ```\n      placeholder: Please do not remove the grave accents; simply replace the line of text in the middle.\n    validations:\n      required: false\n\n  - type: textarea\n    attributes:\n      label: Bug Description\n      description: |\n        Describe the unexpected behavior.\n        If you want to attach screenshots, use the comment field at the bottom of the page.\n      placeholder: |\n        Example: \"Placing signs on 1.16.5 causes text to disappear.\"\n    validations:\n      required: true\n\n  - type: textarea\n    attributes:\n      label: Steps to Reproduce\n      description: |\n        List the steps on how we can reproduce the issue. Make sure we can easily understand what you mean with each step.\n        **Please always include the client version(s) with which the issue happens. Ideally also try to find out from what specific version downward/in what version range the issue happens.**\n      placeholder: |\n        Example:\n        1. Login with a 1.16.5 client\n        2. Place a sign\n        3. The sign text disappears\n    validations:\n      required: true\n\n  - type: textarea\n    attributes:\n      label: Additional Server Info\n      description: |\n        Do you use a proxy (eg. Velocity)? What software do you use and what plugins?\n      placeholder: |\n        Example: \"I also use Velocity with the following plugins: x, y, z\"\n    validations:\n      required: false\n\n  - type: checkboxes\n    attributes:\n      label: Checklist\n      description: Make sure you have followed each of the steps outlined here.\n      options:\n        - label: Via plugins are only running on **EITHER** the backend servers (e.g. Paper) **OR** the proxy (e.g. Velocity), **not on both**.\n          required: true\n        - label: I have included a ViaVersion dump.\n          required: true\n        - label: If applicable, I have included a paste (**not a screenshot**) of the error.\n          required: true\n        - label: I have tried a build from https://ci.viaversion.com/ and the issue still persists.\n          required: true\n\n  - type: markdown\n    attributes:\n      value: |\n        ## Comments And Screenshots\n        If needed, add **screenshots to help explain your problem** in the comment field below.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\ncontact_links:\n  - name: Dev builds\n    url: https://ci.viaversion.com/\n    about: Before reporting a bug, please check if using master/dev builds from our ci fixes your issue.\n  - name: ViaVersion Discord\n    url: https://discord.gg/viaversion\n    about: For smaller issues or questions, you can also join our Discord server."
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.yml",
    "content": "name: Feature Request\ndescription: Suggest a feature to be added\nlabels: [Feature Request]\n\nbody:\n  - type: textarea\n    attributes:\n      label: Problem Description\n      description: |\n        Describe the issue you are facing or why you need the feature to be added.\n      placeholder: |\n        I am always frustrated with...\n    validations:\n      required: true\n\n  - type: textarea\n    attributes:\n      label: Solution Description\n      description: |\n        Describe the solution you would like to see.\n    validations:\n      required: true\n\n  - type: textarea\n    attributes:\n      label: Alternatives\n      description: |\n        Describe alternatives you have considered.\n    validations:\n      required: false\n\n  - type: textarea\n    attributes:\n      label: Additional Info\n      description: |\n        Does the feature apply to any specific version or environment?\n    validations:\n      required: false\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n  - package-ecosystem: \"github-actions\"\n    cooldown:\n      default-days: 7\n    directory: \"/\"\n    schedule:\n      interval: \"monthly\"\n  - package-ecosystem: \"gradle\"\n    cooldown:\n      default-days: 7\n    directory: \"/\"\n    schedule:\n      interval: \"monthly\"\n"
  },
  {
    "path": ".github/workflows/build.yml",
    "content": "name: Build\non: [pull_request, push, workflow_dispatch]\npermissions:\n  contents: read\n\njobs:\n  build:\n    # Only run on PRs if the source branch is on a different repo. We do not need to run everything twice.\n    if: ${{ github.event_name != 'pull_request' || github.repository != github.event.pull_request.head.repo.full_name }}\n    runs-on: ubuntu-24.04\n    timeout-minutes: 30\n    steps:\n    - name: Checkout Repository\n      uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 6.0.2\n      with:\n        persist-credentials: false\n    - name: Set up Gradle\n      uses: gradle/actions/setup-gradle@f29f5a9d7b09a7c6b29859002d29d24e1674c884 # 5.0.1\n    - name: Set up JDK 17\n      uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # 5.2.0\n      with:\n        distribution: 'temurin'\n        java-version: 17\n        check-latest: true\n    - name: Build with Gradle\n      run: ./gradlew build --refresh-dependencies\n"
  },
  {
    "path": ".github/workflows/publish.yml",
    "content": "name: Publish to Hangar and Modrinth\non:\n  push:\n    branches:\n      - master\n      - dev\n  workflow_dispatch:\n\npermissions:\n  contents: read\n\njobs:\n  publish:\n    if: github.repository_owner == 'ViaVersion'\n    runs-on: ubuntu-24.04\n    timeout-minutes: 30\n    steps:\n      - name: Checkout Repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 6.0.2\n        with:\n          persist-credentials: false\n      - name: Set up Gradle\n        uses: gradle/actions/setup-gradle@f29f5a9d7b09a7c6b29859002d29d24e1674c884 # 5.0.1\n      - name: Set up JDK 17\n        uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # 5.2.0\n        with:\n          distribution: 'temurin'\n          java-version: 17\n          check-latest: true\n      - name: Build with Gradle\n        run: ./gradlew build --refresh-dependencies\n      - name: Publish to Hangar\n        env:\n          HANGAR_TOKEN: ${{ secrets.HANGAR_TOKEN }}\n        run: ./gradlew publishAllPublicationsToHangar --stacktrace\n        continue-on-error: true\n      - name: Publish to Modrinth\n        env:\n          MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }}\n        run: ./gradlew modrinth --stacktrace\n        continue-on-error: true\n"
  },
  {
    "path": ".gitignore",
    "content": "# Gradle\n\n.gradle/\nbuild/\nout/\nclasses/\n.kotlin/\n\n# Eclipse\n\n*.launch\n\n# Idea\n\n.idea/\n!.idea/copyright/*\n!.idea/scopes/*\n*.iml\n*.ipr\n*.iws\n\n# VSCode\n\n.settings/\n.vscode/\nbin/\n.classpath\n.project\n\n# macOS\n\n.DS_Store\n\n# Misc\n\nrun/\n"
  },
  {
    "path": "LICENSE",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU General Public License is a free, copyleft license for\nsoftware and other kinds of works.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nthe GNU General Public License is intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.  We, the Free Software Foundation, use the\nGNU General Public License for most of our software; it applies also to\nany other work released this way by its authors.  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  To protect your rights, we need to prevent others from denying you\nthese rights or asking you to surrender the rights.  Therefore, you have\ncertain responsibilities if you distribute copies of the software, or if\nyou modify it: responsibilities to respect the freedom of others.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must pass on to the recipients the same\nfreedoms that you received.  You must make sure that they, too, receive\nor can get the source code.  And you must show them these terms so they\nknow their rights.\n\n  Developers that use the GNU GPL protect your rights with two steps:\n(1) assert copyright on the software, and (2) offer you this License\ngiving you legal permission to copy, distribute and/or modify it.\n\n  For the developers' and authors' protection, the GPL clearly explains\nthat there is no warranty for this free software.  For both users' and\nauthors' sake, the GPL requires that modified versions be marked as\nchanged, so that their problems will not be attributed erroneously to\nauthors of previous versions.\n\n  Some devices are designed to deny users access to install or run\nmodified versions of the software inside them, although the manufacturer\ncan do so.  This is fundamentally incompatible with the aim of\nprotecting users' freedom to change the software.  The systematic\npattern of such abuse occurs in the area of products for individuals to\nuse, which is precisely where it is most unacceptable.  Therefore, we\nhave designed this version of the GPL to prohibit the practice for those\nproducts.  If such problems arise substantially in other domains, we\nstand ready to extend this provision to those domains in future versions\nof the GPL, as needed to protect the freedom of users.\n\n  Finally, every program is threatened constantly by software patents.\nStates should not allow patents to restrict development and use of\nsoftware on general-purpose computers, but in those that do, we wish to\navoid the special danger that patents applied to a free program could\nmake it effectively proprietary.  To prevent this, the GPL assures that\npatents cannot be used to render the program non-free.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Use with the GNU Affero General Public License.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU Affero General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the special requirements of the GNU Affero General Public License,\nsection 13, concerning interaction through a network will apply to the\ncombination as such.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If the program does terminal interaction, make it output a short\nnotice like this when it starts in an interactive mode:\n\n    <program>  Copyright (C) <year>  <name of author>\n    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, your program's commands\nmight be different; for a GUI interface, you would use an \"about box\".\n\n  You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU GPL, see\n<https://www.gnu.org/licenses/>.\n\n  The GNU General Public License does not permit incorporating your program\ninto proprietary programs.  If your program is a subroutine library, you\nmay consider it more useful to permit linking proprietary applications with\nthe library.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.  But first, please read\n<https://www.gnu.org/licenses/why-not-lgpl.html>."
  },
  {
    "path": "README.md",
    "content": "# ViaBackwards\n\n[![Latest Release](https://img.shields.io/github/v/release/ViaVersion/ViaBackwards)](https://github.com/ViaVersion/ViaBackwards/releases)\n[![Build Status](https://github.com/ViaVersion/ViaBackwards/actions/workflows/build.yml/badge.svg?branch=master)](https://github.com/ViaVersion/ViaBackwards/actions)\n[![Discord](https://img.shields.io/badge/chat-on%20discord-blue.svg)](https://viaversion.com/discord)\n\n**Allows the connection of older clients to newer server versions for Minecraft servers.**\n\nRequires [ViaVersion](https://hangar.papermc.io/ViaVersion/ViaVersion) to be installed.\n\nSupported Versions\n-\nAs a plugin, ViaBackwards runs on servers on releases 1.10-latest. You can also use ViaBackwards in ViaFabric or ViaFabricPlus.\n- in **ViaFabric**, put ViaBackwards into the `mods` folder\n- in **ViaFabricPlus**, put ViaBackwards into the `config/viafabriclus/jars` folder\n\nSee [HERE](https://viaversion.com) for an overview of the different Via* projects.\n\nSnapshot support\n--------\n**ViaBackwards will only be released a few days *after* a Minecraft update** unless the protocol changes of the update were trivial. If you want early-access, usually days or even weeks before the final release, you can subscribe to either:\n- [GitHub Sponsors](https://github.com/sponsors/kennytv/sponsorships?sponsor=kennytv&tier_id=385613&preview=false) (preferred option. Use the `/verify` command on this Discord after), or alternatively\n- [Patreon](https://www.patreon.com/kennytv/membership) (see the highest tier and make sure to link Patreon to your Discord account under Settings->Connections)\n  This also includes access to a private repository with the code, which will be pushed to the public repository after the given delay on a Minecraft update.\n\nReleases/Dev Builds\n-\nYou can find releases in the following places:\n\n- **Hangar (for our plugins)**: https://hangar.papermc.io/ViaVersion/ViaBackwards\n- **Modrinth (for our mods)**: https://modrinth.com/mod/viabackwards\n- **GitHub**: https://github.com/ViaVersion/ViaBackwards/releases\n\nDev builds for **all** of our projects are on our Jenkins server:\n\n- **Jenkins**: https://ci.viaversion.com/view/ViaBackwards/\n\nKnown issues\n-\n\n* 1.17+ min_y and height world values that are not 0/256 **are not supported**. Clients older than\n  1.17 will not be able to see or interact with blocks below y=0 and above y=255\n* <1.17 clients on 1.17+ servers might experience inventory desyncs on certain inventory click actions\n* Sound mappings are incomplete ([see here](https://github.com/ViaVersion/ViaBackwards/issues/326))\n* <1.19.4 clients on 1.20+ servers won't be able to use the smithing table. This can be fixed by\n  installing [AxSmithing](https://github.com/ViaVersionAddons/AxSmithing)\n\nOther Links\n-\n**Maven:** https://repo.viaversion.com\n\n**List of contributors:** https://github.com/ViaVersion/ViaBackwards/graphs/contributors\n\nBuilding\n-\nAfter cloning this repository, build the project with Gradle by running `./gradlew build` and take the created jar out\nof the `build/libs` directory.\n\nYou need JDK 17 or newer to build ViaBackwards.\n\nLicense\n-\nThis project is licensed under the [GNU General Public License Version 3](LICENSE).\n\nSpecial Thanks\n-\n![https://www.yourkit.com/](https://www.yourkit.com/images/yklogo.png)\n\n[YourKit](https://www.yourkit.com/) supports open source projects with innovative and intelligent tools\nfor monitoring and profiling Java and .NET applications.\nYourKit is the creator of [YourKit Java Profiler](https://www.yourkit.com/java/profiler/),\n[YourKit .NET Profiler](https://www.yourkit.com/.net/profiler/),\nand [YourKit YouMonitor](https://www.yourkit.com/youmonitor/).\n"
  },
  {
    "path": "SECURITY.md",
    "content": "# Security Policy\n\n## Reporting a Vulnerability\n\nIf you discover a **security vulnerability**, please **do not** open a public issue. Instead, use one of the following **confidential contact methods**:\n\n* **Email:** [security@viaversion.com](mailto:security@viaversion.com)\n* **Discord:** Join our [Discord server](https://discord.gg/viaversion) and visit the [#exploit-report](https://discord.com/channels/316206679014244363/1388847764137316485) channel for further instructions.\n\nIf your issue is **not security-related** (e.g., bug reports, feature requests, or usage questions), please use the [GitHub Issues page](https://github.com/ViaVersion/ViaBackwards/issues) or contact us via the [Discord server](https://discord.gg/viaversion).\n"
  },
  {
    "path": "build-logic/build.gradle.kts",
    "content": "plugins {\n    `kotlin-dsl`\n}\n\nrepositories {\n    gradlePluginPortal()\n}\n\ndependencies {\n    // version must be manually kept in sync with the one in root project settings.gradle.kts\n    implementation(\"com.gradleup.shadow:shadow-gradle-plugin:9.4.1\")\n\n    // A nice no-conflict comment for patching in downgrading\n}\n"
  },
  {
    "path": "build-logic/src/main/kotlin/extensions.kt",
    "content": "import org.gradle.api.Project\nimport org.gradle.api.plugins.JavaPluginExtension\nimport org.gradle.api.provider.ListProperty\nimport org.gradle.api.provider.ValueSource\nimport org.gradle.api.provider.ValueSourceParameters\nimport org.gradle.jvm.toolchain.JavaLanguageVersion\nimport org.gradle.process.ExecOperations\nimport java.io.ByteArrayOutputStream\nimport javax.inject.Inject\n\nfun Project.latestCommitHash(): String {\n    return runGitCommand(listOf(\"rev-parse\", \"--short\", \"HEAD\"))\n}\n\nfun Project.latestCommitMessage(): String {\n    return runGitCommand(listOf(\"log\", \"-1\", \"--pretty=%B\"))\n}\n\nfun Project.branchName(): String {\n    return runGitCommand(listOf(\"rev-parse\", \"--abbrev-ref\", \"HEAD\"))\n}\n\nfun Project.runGitCommand(args: List<String>): String {\n    return providers.of(GitCommand::class.java) { parameters.args.set(args) }.getOrNull() ?: \"unknown\"\n}\n\nabstract class GitCommand : ValueSource<String, GitCommand.GitCommandParameters> {\n\n    @get:Inject\n    abstract val execOperations: ExecOperations\n\n    interface GitCommandParameters : ValueSourceParameters {\n        val args: ListProperty<String>\n    }\n\n    override fun obtain(): String? {\n        try {\n            val command = listOf(\"git\") + parameters.args.get()\n            val output = ByteArrayOutputStream()\n            execOperations.exec {\n                commandLine = command\n                standardOutput = output\n                isIgnoreExitValue = true\n            }\n\n            return output.toString(Charsets.UTF_8).trim().takeIf { it.isNotBlank() }\n        } catch (e: Exception) {\n            return null\n        }\n    }\n}\n\n\nfun Project.parseMinecraftSnapshotVersion(version: String): String? {\n    val separatorIndex = version.indexOf('-')\n    val lastSeparatorIndex = version.lastIndexOf('-')\n    if (separatorIndex == -1 || separatorIndex == lastSeparatorIndex) {\n        return null\n    }\n    return version.substring(separatorIndex + 1, lastSeparatorIndex)\n}\n\nfun JavaPluginExtension.javaTarget(version: Int) {\n    toolchain.languageVersion.set(JavaLanguageVersion.of(version))\n}\n"
  },
  {
    "path": "build-logic/src/main/kotlin/vb.base-conventions.gradle.kts",
    "content": "plugins {\n    `java-library`\n}\n\ntasks {\n    // Variable replacements\n    processResources {\n        val ver = project.version.toString()\n        val desc = project.description\n        filesMatching(listOf(\"plugin.yml\", \"META-INF/sponge_plugins.json\", \"fabric.mod.json\")) {\n            expand(mapOf(\"version\" to ver, \"description\" to desc))\n        }\n    }\n    javadoc {\n        options.encoding = Charsets.UTF_8.name()\n        (options as StandardJavadocDocletOptions).addStringOption(\"Xdoclint:none\", \"-quiet\")\n    }\n    compileJava {\n        options.encoding = Charsets.UTF_8.name()\n        options.compilerArgs.addAll(listOf(\"-nowarn\", \"-Xlint:-unchecked\", \"-Xlint:-deprecation\"))\n        options.isFork = true\n    }\n}\n\njava {\n    javaTarget(17)\n    withSourcesJar()\n}\n"
  },
  {
    "path": "build-logic/src/main/kotlin/vb.build-logic.gradle.kts",
    "content": ""
  },
  {
    "path": "build-logic/src/main/kotlin/vb.shadow-conventions.gradle.kts",
    "content": "import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar\nimport org.gradle.jvm.tasks.Jar\nimport org.gradle.kotlin.dsl.named\n\nplugins {\n    id(\"vb.base-conventions\")\n    id(\"maven-publish\")\n    id(\"com.gradleup.shadow\")\n}\n\ntasks {\n    named<Jar>(\"jar\") {\n        archiveClassifier.set(\"unshaded\")\n        from(project.rootProject.file(\"LICENSE\"))\n    }\n    val shadowJar = named<ShadowJar>(\"shadowJar\") {\n        duplicatesStrategy = DuplicatesStrategy.EXCLUDE\n        archiveClassifier.set(\"\")\n        configureRelocations()\n    }\n    named(\"build\") {\n        dependsOn(shadowJar)\n    }\n}\n\npublishing {\n    publications.create<MavenPublication>(\"mavenJava\") {\n        groupId = rootProject.group as String\n        artifactId = project.name\n        version = rootProject.version as String\n\n        artifact(tasks[\"shadowJar\"])\n        artifact(tasks[\"sourcesJar\"])\n    }\n    repositories.maven {\n        name = \"Via\"\n        url = uri(\"https://repo.viaversion.com/\")\n        credentials(PasswordCredentials::class)\n        authentication {\n            create<BasicAuthentication>(\"basic\")\n        }\n    }\n}\n\nfun ShadowJar.configureRelocations() {\n    relocate(\"com.google.gson\", \"com.viaversion.viaversion.libs.gson\")\n    relocate(\"it.unimi.dsi.fastutil\", \"com.viaversion.viaversion.libs.fastutil\")\n}\n"
  },
  {
    "path": "build.gradle.kts",
    "content": "plugins {\n    base\n    id(\"vb.build-logic\")\n}\n\nallprojects {\n    group = \"com.viaversion\"\n    version = property(\"projectVersion\") as String // from gradle.properties\n    description = \"Allows the connection of older clients to newer server versions for Minecraft servers.\"\n}\n\nval main = setOf(\n    projects.viabackwards,\n    projects.viabackwardsCommon,\n    projects.viabackwardsBukkit,\n    projects.viabackwardsVelocity\n).map { it.path }\n\nsubprojects {\n    when (path) {\n        in main -> plugins.apply(\"vb.shadow-conventions\")\n        else -> plugins.apply(\"vb.base-conventions\")\n    }\n}\n"
  },
  {
    "path": "bukkit/build.gradle.kts",
    "content": "dependencies {\n    compileOnlyApi(projects.viabackwardsCommon)\n    compileOnly(libs.paper) {\n        exclude(\"com.google.code.gson\", \"gson\")\n        exclude(\"javax.persistence\", \"persistence-api\")\n    }\n}\n"
  },
  {
    "path": "bukkit/src/main/java/com/viaversion/viabackwards/BukkitPlugin.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.viaversion.viabackwards;\n\nimport com.viaversion.viabackwards.api.ViaBackwardsPlatform;\nimport com.viaversion.viabackwards.listener.DurabilitySync1_11;\nimport com.viaversion.viabackwards.listener.PlayerHurtSound1_12;\nimport com.viaversion.viabackwards.listener.FireExtinguish1_16;\nimport com.viaversion.viabackwards.listener.LecternInteract1_14;\nimport com.viaversion.viabackwards.listener.ItemDropSync1_17;\nimport com.viaversion.viabackwards.listener.SpearAttack1_21_11;\nimport com.viaversion.viabackwards.protocol.v1_20_2to1_20.provider.AdvancementCriteriaProvider;\nimport com.viaversion.viabackwards.provider.BukkitAdvancementCriteriaProvider;\nimport com.viaversion.viaversion.api.Via;\nimport com.viaversion.viaversion.api.platform.providers.ViaProviders;\nimport com.viaversion.viaversion.api.protocol.version.ProtocolVersion;\nimport java.io.File;\nimport org.bukkit.plugin.java.JavaPlugin;\n\npublic class BukkitPlugin extends JavaPlugin implements ViaBackwardsPlatform {\n\n    public BukkitPlugin() {\n        Via.getManager().addEnableListener(() -> init(new File(getDataFolder(), \"config.yml\")));\n    }\n\n    @Override\n    public void onEnable() {\n        if (Via.getManager().getInjector().lateProtocolVersionSetting()) {\n            // Enable in the next tick\n            Via.getPlatform().runSync(this::enable, 1);\n        } else {\n            enable();\n        }\n    }\n\n    @Override\n    public void enable() {\n        ViaBackwardsPlatform.super.enable();\n\n        final ProtocolVersion protocolVersion = Via.getAPI().getServerVersion().highestSupportedProtocolVersion();\n        if (protocolVersion.newerThanOrEqualTo(ProtocolVersion.v1_21_11)) {\n            new SpearAttack1_21_11(this).register();\n        }\n        if (protocolVersion.newerThanOrEqualTo(ProtocolVersion.v1_17)) {\n            new ItemDropSync1_17(this).register();\n        }\n        if (protocolVersion.newerThanOrEqualTo(ProtocolVersion.v1_16)) {\n            new FireExtinguish1_16(this).register();\n        }\n        if (protocolVersion.newerThanOrEqualTo(ProtocolVersion.v1_14)) {\n            new LecternInteract1_14(this).register();\n        }\n        if (protocolVersion.newerThanOrEqualTo(ProtocolVersion.v1_12)) {\n            new PlayerHurtSound1_12(this).register();\n        }\n        if (protocolVersion.newerThanOrEqualTo(ProtocolVersion.v1_11)) {\n            new DurabilitySync1_11(this).register();\n        }\n\n        final ViaProviders providers = Via.getManager().getProviders();\n        if (protocolVersion.newerThanOrEqualTo(ProtocolVersion.v1_20_2)) {\n            providers.use(AdvancementCriteriaProvider.class, new BukkitAdvancementCriteriaProvider());\n        }\n    }\n\n    @Override\n    public void disable() {\n        getPluginLoader().disablePlugin(this);\n    }\n}\n"
  },
  {
    "path": "bukkit/src/main/java/com/viaversion/viabackwards/listener/DurabilitySync1_11.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.listener;\n\nimport com.viaversion.viabackwards.BukkitPlugin;\nimport com.viaversion.viabackwards.protocol.v1_11to1_10.Protocol1_11To1_10;\nimport com.viaversion.viaversion.bukkit.listeners.ViaBukkitListener;\nimport org.bukkit.GameMode;\nimport org.bukkit.entity.Player;\nimport org.bukkit.event.EventHandler;\nimport org.bukkit.event.EventPriority;\nimport org.bukkit.event.block.BlockBreakEvent;\nimport org.bukkit.inventory.ItemStack;\n\npublic class DurabilitySync1_11 extends ViaBukkitListener {\n\n    public DurabilitySync1_11(final BukkitPlugin plugin) {\n        super(plugin, Protocol1_11To1_10.class);\n    }\n\n    @EventHandler(priority = EventPriority.MONITOR)\n    public void onBlockBreak(final BlockBreakEvent event) {\n        if (!event.isCancelled()) {\n            return;\n        }\n\n        final Player player = event.getPlayer();\n        if (player.getGameMode() == GameMode.CREATIVE || !isOnPipe(player)) {\n            return;\n        }\n\n        // Resend the item in the hand to sync durability\n        final int slot = player.getInventory().getHeldItemSlot();\n        final ItemStack item = player.getInventory().getItem(slot);\n        if (item != null && item.getType().getMaxDurability() > 0) {\n            player.getInventory().setItem(slot, item);\n        }\n    }\n}\n"
  },
  {
    "path": "bukkit/src/main/java/com/viaversion/viabackwards/listener/FireExtinguish1_16.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.listener;\n\nimport com.viaversion.viabackwards.BukkitPlugin;\nimport com.viaversion.viabackwards.protocol.v1_16to1_15_2.Protocol1_16To1_15_2;\nimport com.viaversion.viaversion.bukkit.listeners.ViaBukkitListener;\nimport org.bukkit.Material;\nimport org.bukkit.block.Block;\nimport org.bukkit.entity.Player;\nimport org.bukkit.event.EventHandler;\nimport org.bukkit.event.EventPriority;\nimport org.bukkit.event.block.Action;\nimport org.bukkit.event.player.PlayerInteractEvent;\n\npublic class FireExtinguish1_16 extends ViaBukkitListener {\n\n    public FireExtinguish1_16(BukkitPlugin plugin) {\n        super(plugin, Protocol1_16To1_15_2.class);\n    }\n\n    @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)\n    public void onFireExtinguish(PlayerInteractEvent event) {\n        if (event.getAction() != Action.LEFT_CLICK_BLOCK) return;\n\n        Block block = event.getClickedBlock();\n        if (block == null) return;\n\n        Player player = event.getPlayer();\n        if (!isOnPipe(player)) return;\n\n        Block relative = block.getRelative(event.getBlockFace());\n        if (relative.getType() == Material.FIRE) {\n            event.setCancelled(true);\n            relative.setType(Material.AIR);\n        }\n    }\n}\n"
  },
  {
    "path": "bukkit/src/main/java/com/viaversion/viabackwards/listener/ItemDropSync1_17.java",
    "content": "\n/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.listener;\n\nimport com.viaversion.viabackwards.BukkitPlugin;\nimport com.viaversion.viabackwards.protocol.v1_13_1to1_13.Protocol1_13_1To1_13;\nimport com.viaversion.viaversion.bukkit.listeners.ViaBukkitListener;\nimport org.bukkit.entity.Player;\nimport org.bukkit.event.EventHandler;\nimport org.bukkit.event.EventPriority;\nimport org.bukkit.event.player.PlayerDropItemEvent;\nimport org.bukkit.inventory.ItemStack;\n\npublic class ItemDropSync1_17 extends ViaBukkitListener {\n\n    public ItemDropSync1_17(final BukkitPlugin plugin) {\n        super(plugin, Protocol1_13_1To1_13.class); // Starts with 1.13 clients on 1.17 servers\n    }\n\n    @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)\n    public void onItemDrop(final PlayerDropItemEvent event) {\n        final Player player = event.getPlayer();\n        if (!isOnPipe(player)) {\n            return;\n        }\n\n        // Resend the item in the hand\n        final int slot = player.getInventory().getHeldItemSlot();\n        final ItemStack item = player.getInventory().getItem(slot);\n        player.getInventory().setItem(slot, item);\n    }\n}\n"
  },
  {
    "path": "bukkit/src/main/java/com/viaversion/viabackwards/listener/LecternInteract1_14.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.listener;\n\nimport com.viaversion.viabackwards.BukkitPlugin;\nimport com.viaversion.viabackwards.protocol.v1_14to1_13_2.Protocol1_14To1_13_2;\nimport com.viaversion.viaversion.bukkit.listeners.ViaBukkitListener;\nimport org.bukkit.Material;\nimport org.bukkit.block.Block;\nimport org.bukkit.block.Lectern;\nimport org.bukkit.entity.Player;\nimport org.bukkit.event.EventHandler;\nimport org.bukkit.event.EventPriority;\nimport org.bukkit.event.player.PlayerInteractEvent;\nimport org.bukkit.inventory.ItemStack;\nimport org.bukkit.inventory.meta.BookMeta;\n\npublic class LecternInteract1_14 extends ViaBukkitListener {\n\n    public LecternInteract1_14(BukkitPlugin plugin) {\n        super(plugin, Protocol1_14To1_13_2.class);\n    }\n\n    @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)\n    public void onLecternInteract(PlayerInteractEvent event) {\n        Block block = event.getClickedBlock();\n        if (block == null || block.getType() != Material.LECTERN) return;\n\n        Player player = event.getPlayer();\n        if (!isOnPipe(player)) return;\n\n        Lectern lectern = (Lectern) block.getState();\n        ItemStack book = lectern.getInventory().getItem(0);\n        if (book == null) return;\n\n        BookMeta meta = (BookMeta) book.getItemMeta();\n\n        // Open a book with the text of the lectern's writable book\n        ItemStack newBook = new ItemStack(Material.WRITTEN_BOOK);\n        BookMeta newBookMeta = (BookMeta) newBook.getItemMeta();\n        //noinspection deprecation\n        newBookMeta.setPages(meta.getPages());\n        newBookMeta.setAuthor(\"an upsidedown person\");\n        newBookMeta.setTitle(\"buk\");\n        newBook.setItemMeta(newBookMeta);\n        player.openBook(newBook);\n\n        event.setCancelled(true);\n    }\n}\n"
  },
  {
    "path": "bukkit/src/main/java/com/viaversion/viabackwards/listener/PlayerHurtSound1_12.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.listener;\n\nimport com.viaversion.viabackwards.BukkitPlugin;\nimport com.viaversion.viabackwards.protocol.v1_12to1_11_1.Protocol1_12To1_11_1;\nimport com.viaversion.viaversion.bukkit.listeners.ViaBukkitListener;\nimport org.bukkit.Sound;\nimport org.bukkit.SoundCategory;\nimport org.bukkit.entity.EntityType;\nimport org.bukkit.entity.Player;\nimport org.bukkit.event.EventHandler;\nimport org.bukkit.event.EventPriority;\nimport org.bukkit.event.entity.EntityDamageEvent;\n\npublic class PlayerHurtSound1_12 extends ViaBukkitListener {\n\n    public PlayerHurtSound1_12(BukkitPlugin plugin) {\n        super(plugin, Protocol1_12To1_11_1.class);\n    }\n\n    @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)\n    public void onFireDamage(EntityDamageEvent event) {\n        if (event.getEntityType() != EntityType.PLAYER) return;\n\n        EntityDamageEvent.DamageCause cause = event.getCause();\n        if (cause != EntityDamageEvent.DamageCause.FIRE\n            && cause != EntityDamageEvent.DamageCause.FIRE_TICK\n            && cause != EntityDamageEvent.DamageCause.LAVA\n            && cause != EntityDamageEvent.DamageCause.DROWNING) {\n            return;\n        }\n\n        Player player = (Player) event.getEntity();\n        if (isOnPipe(player)) {\n            player.playSound(player.getLocation(), Sound.ENTITY_PLAYER_HURT, SoundCategory.PLAYERS, 1, 1);\n        }\n    }\n}\n"
  },
  {
    "path": "bukkit/src/main/java/com/viaversion/viabackwards/listener/SpearAttack1_21_11.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.listener;\n\nimport com.viaversion.viabackwards.BukkitPlugin;\nimport com.viaversion.viabackwards.protocol.v1_21_11to1_21_9.Protocol1_21_11To1_21_9;\nimport com.viaversion.viaversion.bukkit.listeners.ViaBukkitListener;\nimport org.bukkit.entity.Player;\nimport org.bukkit.event.EventHandler;\nimport org.bukkit.event.EventPriority;\nimport org.bukkit.event.block.BlockBreakEvent;\nimport org.bukkit.inventory.ItemStack;\n\npublic class SpearAttack1_21_11 extends ViaBukkitListener {\n\n    public SpearAttack1_21_11(final BukkitPlugin plugin) {\n        super(plugin, Protocol1_21_11To1_21_9.class);\n    }\n\n    @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)\n    public void onBlockBreak(final BlockBreakEvent event) {\n        final Player player = event.getPlayer();\n        if (!isOnPipe(player)) {\n            return;\n        }\n\n        final ItemStack item = player.getInventory().getItemInMainHand();\n        if (item.getType().name().endsWith(\"_SPEAR\")) {\n            // Prevent spears from breaking blocks\n            event.setCancelled(true);\n        }\n    }\n}\n"
  },
  {
    "path": "bukkit/src/main/java/com/viaversion/viabackwards/provider/BukkitAdvancementCriteriaProvider.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.provider;\n\nimport com.viaversion.viabackwards.protocol.v1_20_2to1_20.provider.AdvancementCriteriaProvider;\nimport org.bukkit.Bukkit;\nimport org.bukkit.NamespacedKey;\nimport org.bukkit.advancement.Advancement;\n\npublic final class BukkitAdvancementCriteriaProvider extends AdvancementCriteriaProvider {\n\n    private static final String[] EMPTY_CRITERIA = new String[0];\n\n    @Override\n    public String[] getCriteria(final String advancementKey) {\n        final Advancement advancement = Bukkit.getAdvancement(NamespacedKey.fromString(advancementKey));\n        return advancement == null ? EMPTY_CRITERIA : advancement.getCriteria().toArray(EMPTY_CRITERIA);\n    }\n}\n"
  },
  {
    "path": "bukkit/src/main/resources/plugin.yml",
    "content": "name: ViaBackwards\nversion: ${version}\ndescription: ${description}\nmain: com.viaversion.viabackwards.BukkitPlugin\napi-version: 1.13\nfolia-supported: true\n\nauthors: [Matsv, kennytv, Gerrygames, creeper123123321, ForceUpdate1, EnZaXD]\nwebsite: \"https://viaversion.com/backwards\"\n\ndepend: [ViaVersion]\n"
  },
  {
    "path": "common/build.gradle.kts",
    "content": "plugins {\n    id(\"net.kyori.blossom\")\n    id(\"org.jetbrains.gradle.plugin.idea-ext\")\n}\n\nsourceSets {\n    main {\n        blossom {\n            javaSources {\n                property(\"version\", project.version.toString())\n                property(\"impl_version\", \"git-ViaBackwards-${project.version}:${rootProject.latestCommitHash()}\")\n            }\n        }\n    }\n}\n\ndependencies {\n    compileOnlyApi(libs.viaver)\n    compileOnlyApi(libs.netty)\n    compileOnlyApi(libs.guava)\n    compileOnlyApi(libs.checkerQual)\n}\n\njava {\n    withJavadocJar()\n}\n\n// Task to quickly test/debug code changes using https://github.com/ViaVersion/ViaProxy\n// For further instructions see the ViaProxy repository README\nval prepareViaProxyFiles by tasks.registering(Copy::class) {\n    dependsOn(project.tasks.shadowJar)\n\n    from(project.tasks.shadowJar.map { it.archiveFile.get().asFile })\n    into(layout.projectDirectory.dir(\"run/jars\"))\n\n    val projectName = project.name\n    rename { \"${projectName}.jar\" }\n}\n\nval cleanupViaProxyFiles by tasks.registering(Delete::class) {\n    delete(\n        layout.projectDirectory.file(\"run/jars/${project.name}.jar\"),\n        layout.projectDirectory.dir(\"run/logs\")\n    )\n}\n\nval viaProxyConfiguration: Configuration by configurations.creating {\n    dependencies.add(rootProject.libs.viaProxy.get().copy().setTransitive(false))\n}\n\ntasks.register<JavaExec>(\"runViaProxy\") {\n    dependsOn(prepareViaProxyFiles)\n    finalizedBy(cleanupViaProxyFiles)\n\n    mainClass.set(\"net.raphimc.viaproxy.ViaProxy\")\n    classpath = viaProxyConfiguration\n    workingDir = layout.projectDirectory.dir(\"run\").asFile\n    jvmArgs = listOf(\"-DskipUpdateCheck\")\n\n    if (System.getProperty(\"viaproxy.gui.autoStart\") != null) {\n        jvmArgs(\"-Dviaproxy.gui.autoStart\")\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/ViaBackwards.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.viaversion.viabackwards;\n\nimport com.google.common.base.Preconditions;\nimport com.viaversion.viabackwards.api.ViaBackwardsConfig;\nimport com.viaversion.viabackwards.api.ViaBackwardsPlatform;\n\npublic final class ViaBackwards {\n\n    private static ViaBackwardsPlatform platform;\n    private static ViaBackwardsConfig config;\n\n    public static void init(ViaBackwardsPlatform platform, ViaBackwardsConfig config) {\n        Preconditions.checkArgument(ViaBackwards.platform == null, \"ViaBackwards is already initialized\");\n\n        ViaBackwards.platform = platform;\n        ViaBackwards.config = config;\n    }\n\n    public static ViaBackwardsPlatform getPlatform() {\n        return platform;\n    }\n\n    public static ViaBackwardsConfig getConfig() {\n        return config;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/ViaBackwardsConfig.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards;\n\nimport com.viaversion.viabackwards.api.DialogStyleConfig;\nimport com.viaversion.viaversion.util.ChatColorUtil;\nimport com.viaversion.viaversion.util.Config;\nimport com.viaversion.viaversion.util.ConfigSection;\nimport java.io.File;\nimport java.io.InputStream;\nimport java.net.URL;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.logging.Logger;\n\npublic class ViaBackwardsConfig extends Config implements com.viaversion.viabackwards.api.ViaBackwardsConfig {\n\n    private boolean addCustomEnchantsToLore;\n    private boolean addTeamColorToPrefix;\n    private boolean fix1_13FacePlayer;\n    private boolean alwaysShowOriginalMobName;\n    private boolean fix1_13FormattedInventoryTitles;\n    private boolean handlePingsAsInvAcknowledgements;\n    private boolean bedrockAtY0;\n    private boolean sculkShriekersToCryingObsidian;\n    private boolean scaffoldingToWater;\n    private boolean mapDarknessEffect;\n    private boolean mapCustomModelData;\n    private boolean mapDisplayEntities;\n    private boolean suppressEmulationWarnings;\n    private boolean dialogsViaChests;\n    private DialogStyleConfig dialogStyleConfig;\n    private boolean codeOfConductAsDialog;\n    private boolean passOriginalItemNameToResourcePacks;\n\n    public ViaBackwardsConfig(File configFile, Logger logger) {\n        super(configFile, logger);\n    }\n\n    @Override\n    public void reload() {\n        super.reload();\n        loadFields();\n    }\n\n    private void loadFields() {\n        addCustomEnchantsToLore = getBoolean(\"add-custom-enchants-into-lore\", true);\n        addTeamColorToPrefix = getBoolean(\"add-teamcolor-to-prefix\", true);\n        fix1_13FacePlayer = getBoolean(\"fix-1_13-face-player\", false);\n        fix1_13FormattedInventoryTitles = getBoolean(\"fix-formatted-inventory-titles\", true);\n        alwaysShowOriginalMobName = getBoolean(\"always-show-original-mob-name\", true);\n        handlePingsAsInvAcknowledgements = getBoolean(\"handle-pings-as-inv-acknowledgements\", false);\n        bedrockAtY0 = getBoolean(\"bedrock-at-y-0\", false);\n        sculkShriekersToCryingObsidian = getBoolean(\"sculk-shriekers-to-crying-obsidian\", false);\n        scaffoldingToWater = getBoolean(\"scaffolding-to-water\", false);\n        mapDarknessEffect = getBoolean(\"map-darkness-effect\", true);\n        mapCustomModelData = getBoolean(\"map-custom-model-data\", true);\n        mapDisplayEntities = getBoolean(\"map-display-entities\", true);\n        suppressEmulationWarnings = getBoolean(\"suppress-emulation-warnings\", false);\n        dialogsViaChests = getBoolean(\"dialogs-via-chests\", true);\n        dialogStyleConfig = loadDialogStyleConfig(getSection(\"dialog-style\"));\n        codeOfConductAsDialog = getBoolean(\"code-of-conduct-as-dialog\", true);\n        passOriginalItemNameToResourcePacks = getBoolean(\"pass-original-item-name-to-resource-packs\", true);\n    }\n\n    private DialogStyleConfig loadDialogStyleConfig(final ConfigSection section) {\n        return new DialogStyleConfig(\n            getString(section, \"page-navigation-title\", \"&9&lPage navigation\"),\n            getString(section, \"page-navigation-next\", \"&9Left click: &6Go to next page\"),\n            getString(section, \"page-navigation-previous\", \"&9Right click: &6Go to previous page\"),\n            getString(section, \"increase-value\", \"&9Left click: &6Increase value by %s\"),\n            getString(section, \"decrease-value\", \"&9Right click: &6Decrease value by %s\"),\n            getString(section, \"value-range\", \"&7(Value between &a%s &7and &a%s&7)\"),\n            getString(section, \"next-option\", \"&9Left click: &6Go to next option\"),\n            getString(section, \"previous-option\", \"&9Right click: &6Go to previous option\"),\n            getString(section, \"current-value\", \"&7Current value: &a%s\"),\n            getString(section, \"edit-value\", \"&9Left click: &6Edit text\"),\n            getString(section, \"set-text\", \"&9Left click/close: &6Set text\"),\n            getString(section, \"close\", \"&9Left click: &6Close\"),\n            getString(section, \"toggle-value\", \"&9Left click: &6Toggle value\")\n        );\n    }\n\n    protected String getString(final ConfigSection section, final String key, final String def) {\n        return ChatColorUtil.translateAlternateColorCodes(section.getString(key, def));\n    }\n\n    @Override\n    public boolean addCustomEnchantsToLore() {\n        return addCustomEnchantsToLore;\n    }\n\n    @Override\n    public boolean addTeamColorTo1_13Prefix() {\n        return addTeamColorToPrefix;\n    }\n\n    @Override\n    public boolean isFix1_13FacePlayer() {\n        return fix1_13FacePlayer;\n    }\n\n    @Override\n    public boolean fix1_13FormattedInventoryTitle() {\n        return fix1_13FormattedInventoryTitles;\n    }\n\n    @Override\n    public boolean alwaysShowOriginalMobName() {\n        return alwaysShowOriginalMobName;\n    }\n\n    @Override\n    public boolean handlePingsAsInvAcknowledgements() {\n        return handlePingsAsInvAcknowledgements || Boolean.getBoolean(\"com.viaversion.handlePingsAsInvAcknowledgements\");\n    }\n\n    @Override\n    public boolean bedrockAtY0() {\n        return bedrockAtY0;\n    }\n\n    @Override\n    public boolean sculkShriekerToCryingObsidian() {\n        return sculkShriekersToCryingObsidian;\n    }\n\n    @Override\n    public boolean scaffoldingToWater() {\n        return scaffoldingToWater;\n    }\n\n    @Override\n    public boolean mapDarknessEffect() {\n        return mapDarknessEffect;\n    }\n\n    @Override\n    public boolean mapCustomModelData() {\n        return mapCustomModelData;\n    }\n\n    @Override\n    public boolean mapDisplayEntities() {\n        return mapDisplayEntities;\n    }\n\n    @Override\n    public boolean suppressEmulationWarnings() {\n        return suppressEmulationWarnings;\n    }\n\n    @Override\n    public boolean dialogsViaChests() {\n        return dialogsViaChests;\n    }\n\n    @Override\n    public DialogStyleConfig dialogStyleConfig() {\n        return dialogStyleConfig;\n    }\n\n    @Override\n    public boolean codeOfConductAsDialog() {\n        return codeOfConductAsDialog;\n    }\n\n    @Override\n    public boolean passOriginalItemNameToResourcePacks() {\n        return passOriginalItemNameToResourcePacks;\n    }\n\n    @Override\n    public URL getDefaultConfigURL() {\n        return getClass().getClassLoader().getResource(\"assets/viabackwards/config.yml\");\n    }\n\n    @Override\n    public InputStream getDefaultConfigInputStream() {\n        return getClass().getClassLoader().getResourceAsStream(\"assets/viabackwards/config.yml\");\n    }\n\n    @Override\n    protected void handleConfig(Map<String, Object> map) {\n    }\n\n    @Override\n    public List<String> getUnsupportedOptions() {\n        return Collections.emptyList();\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/ViaBackwardsPlatformImpl.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards;\n\nimport com.viaversion.viabackwards.api.ViaBackwardsPlatform;\nimport com.viaversion.viaversion.api.Via;\nimport java.io.File;\nimport java.util.logging.Logger;\n\npublic class ViaBackwardsPlatformImpl implements ViaBackwardsPlatform {\n\n    private final Logger logger;\n\n    public ViaBackwardsPlatformImpl() {\n        logger = Via.getPlatform().createLogger(\"ViaBackwards\");\n        init(new File(getDataFolder(), \"viabackwards.yml\"));\n        enable();\n    }\n\n    @Override\n    public Logger getLogger() {\n        return logger;\n    }\n\n    @Override\n    public void disable() {\n    }\n\n    @Override\n    public File getDataFolder() {\n        return Via.getPlatform().getDataFolder();\n    }\n\n    @Override\n    public boolean isOutdated() {\n        return false;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/api/BackwardsProtocol.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.api;\n\nimport com.viaversion.viabackwards.api.data.BackwardsMappingData;\nimport com.viaversion.viabackwards.api.rewriters.BackwardsRegistryRewriter;\nimport com.viaversion.viabackwards.protocol.registration.BackwardsRegistrations;\nimport com.viaversion.viabackwards.utils.BackwardsProtocolLogger;\nimport com.viaversion.viaversion.api.protocol.AbstractProtocol;\nimport com.viaversion.viaversion.api.protocol.Protocol;\nimport com.viaversion.viaversion.api.protocol.packet.ClientboundPacketType;\nimport com.viaversion.viaversion.api.protocol.packet.ServerboundPacketType;\nimport com.viaversion.viaversion.util.ProtocolLogger;\nimport org.checkerframework.checker.nullness.qual.Nullable;\n\npublic abstract class BackwardsProtocol<CU extends ClientboundPacketType, CM extends ClientboundPacketType, SM extends ServerboundPacketType, SU extends ServerboundPacketType>\n    extends AbstractProtocol<CU, CM, SM, SU> {\n\n    @Deprecated\n    protected BackwardsProtocol() {\n    }\n\n    protected BackwardsProtocol(@Nullable Class<CU> oldClientboundPacketEnum, @Nullable Class<CM> clientboundPacketEnum,\n                                @Nullable Class<SM> oldServerboundPacketEnum, @Nullable Class<SU> serverboundPacketEnum) {\n        super(oldClientboundPacketEnum, clientboundPacketEnum, oldServerboundPacketEnum, serverboundPacketEnum);\n    }\n\n    @Override\n    protected void applySharedRegistrations() {\n        super.applySharedRegistrations();\n        BackwardsRegistrations.registrations().applyMatching(this);\n    }\n\n    @Override\n    protected ProtocolLogger createLogger() {\n        return new BackwardsProtocolLogger(getClass());\n    }\n\n    @Override\n    public @Nullable Class<? extends Protocol<?, ?, ?, ?>> dependsOn() {\n        return getMappingData() != null ? getMappingData().getViaVersionProtocolClass() : null;\n    }\n\n    @Override\n    public @Nullable BackwardsMappingData getMappingData() {\n        return null;\n    }\n\n    @Override\n    public @Nullable BackwardsRegistryRewriter getRegistryDataRewriter() {\n        return null;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/api/DialogStyleConfig.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.api;\n\npublic record DialogStyleConfig(String pageNavigationTitle, String pageNavigationNext, String pageNavigationPrevious,\n                                String increaseValue, String decreaseValue, String valueRange, String nextOption,\n                                String previousOption, String currentValue, String editValue, String setText,\n                                String close, String toggleValue) {\n\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/api/ViaBackwardsConfig.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.api;\n\nimport com.viaversion.viaversion.api.configuration.Config;\n\npublic interface ViaBackwardsConfig extends Config {\n\n    /**\n     * Mimics name and level of a custom enchant through the item's lore.\n     *\n     * @return true if enabled\n     */\n    boolean addCustomEnchantsToLore();\n\n    /**\n     * Writes the color of the scoreboard team after the prefix.\n     *\n     * @return true if enabled\n     */\n    boolean addTeamColorTo1_13Prefix();\n\n    /**\n     * Converts the new 1.13 face player packets to look packets.\n     *\n     * @return true if enabled\n     */\n    boolean isFix1_13FacePlayer();\n\n    /**\n     * Converts the 1.13 face look-at packet for 1.12- players. Requires a bit of extra caching.\n     *\n     * @return true if enabled\n     */\n    boolean fix1_13FormattedInventoryTitle();\n\n    /**\n     * Always shows the original mob's name instead of only when hovering over them with the cursor.\n     *\n     * @return true if enabled\n     */\n    boolean alwaysShowOriginalMobName();\n\n    /**\n     * Sends inventory acknowledgement packets to act as a replacement for ping packets for sub 1.17 clients.\n     * This only takes effect for ids in the short range. Useful for anticheat compatibility.\n     *\n     * @return true if enabled\n     */\n    boolean handlePingsAsInvAcknowledgements();\n\n    /**\n     * Adds bedrock at y=0 for sub 1.17 clients.\n     *\n     * @return true if enabled\n     */\n    boolean bedrockAtY0();\n\n    /**\n     * Shows sculk shriekers as crying obsidian for 1.18.2 clients on 1.19+ servers. This fixes collision and block breaking issues.\n     *\n     * @return true if enabled\n     */\n    boolean sculkShriekerToCryingObsidian();\n\n    /**\n     * Shows scaffolding as water for 1.13.2 clients on 1.14+ servers. This fixes collision issues.\n     *\n     * @return true if enabled\n     */\n    boolean scaffoldingToWater();\n\n    /**\n     * Maps the darkness effect to blindness for 1.18.2 clients on 1.19+ servers.\n     *\n     * @return true if enabled\n     */\n    boolean mapDarknessEffect();\n\n    /**\n     * If enabled, 1.21.3 clients will receive the first float of 1.21.4+ custom model data as int. Disable if you handle this change yourself.\n     *\n     * @return true if enabled\n     */\n    boolean mapCustomModelData();\n\n    /**\n     * If enabled, 1.19.3 clients will receive display entities as armor stands with custom entity data on 1.19.4+ servers.\n     *\n     * @return true if enabled\n     */\n    boolean mapDisplayEntities();\n\n    /**\n     * Suppresses warnings of missing emulations for certain features that are not supported (e.g. world height in 1.17+).\n     *\n     * @return true if enabled\n     */\n    boolean suppressEmulationWarnings();\n\n    /**\n     * If enabled, dialogs will be shown via chest inventories for 1.21.5 clients on 1.21.6+ servers.\n     *\n     * @return true if enabled\n     */\n    boolean dialogsViaChests();\n\n    /**\n     * Returns the dialog style configuration.\n     *\n     * @return the dialog style configuration\n     */\n    DialogStyleConfig dialogStyleConfig();\n\n    /**\n     * If true, the code of conduct will be displayed as a dialog for 1.21.7 clients on 1.21.9+ servers.\n     *\n     * @return true if enabled\n     */\n    boolean codeOfConductAsDialog();\n\n    /**\n     * Injects the original vanilla 1.21.4+ item name into custom_model_data strings for resource packs.\n     * Disable if your server creates custom items using modern items as their base.\n     * Tip: For server custom items, base them on items in the game before 1.21.4 (e.g. saddle) to ensure compatibility.\n     *\n     * @return true if enabled\n     */\n    boolean passOriginalItemNameToResourcePacks();\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/api/ViaBackwardsPlatform.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.viaversion.viabackwards.api;\n\nimport com.viaversion.viabackwards.ViaBackwards;\nimport com.viaversion.viabackwards.ViaBackwardsConfig;\nimport com.viaversion.viabackwards.api.data.TranslatableMappings;\nimport com.viaversion.viabackwards.protocol.registration.BackwardsRegistrations;\nimport com.viaversion.viabackwards.protocol.v1_10to1_9_3.Protocol1_10To1_9_3;\nimport com.viaversion.viabackwards.protocol.v1_11_1to1_11.Protocol1_11_1To1_11;\nimport com.viaversion.viabackwards.protocol.v1_11to1_10.Protocol1_11To1_10;\nimport com.viaversion.viabackwards.protocol.v1_12_1to1_12.Protocol1_12_1To1_12;\nimport com.viaversion.viabackwards.protocol.v1_12_2to1_12_1.Protocol1_12_2To1_12_1;\nimport com.viaversion.viabackwards.protocol.v1_12to1_11_1.Protocol1_12To1_11_1;\nimport com.viaversion.viabackwards.protocol.v1_13_1to1_13.Protocol1_13_1To1_13;\nimport com.viaversion.viabackwards.protocol.v1_13_2to1_13_1.Protocol1_13_2To1_13_1;\nimport com.viaversion.viabackwards.protocol.v1_13to1_12_2.Protocol1_13To1_12_2;\nimport com.viaversion.viabackwards.protocol.v1_14_1to1_14.Protocol1_14_1To1_14;\nimport com.viaversion.viabackwards.protocol.v1_14_2to1_14_1.Protocol1_14_2To1_14_1;\nimport com.viaversion.viabackwards.protocol.v1_14_3to1_14_2.Protocol1_14_3To1_14_2;\nimport com.viaversion.viabackwards.protocol.v1_14_4to1_14_3.Protocol1_14_4To1_14_3;\nimport com.viaversion.viabackwards.protocol.v1_14to1_13_2.Protocol1_14To1_13_2;\nimport com.viaversion.viabackwards.protocol.v1_15_1to1_15.Protocol1_15_1To1_15;\nimport com.viaversion.viabackwards.protocol.v1_15_2to1_15_1.Protocol1_15_2To1_15_1;\nimport com.viaversion.viabackwards.protocol.v1_15to1_14_4.Protocol1_15To1_14_4;\nimport com.viaversion.viabackwards.protocol.v1_16_1to1_16.Protocol1_16_1To1_16;\nimport com.viaversion.viabackwards.protocol.v1_16_2to1_16_1.Protocol1_16_2To1_16_1;\nimport com.viaversion.viabackwards.protocol.v1_16_3to1_16_2.Protocol1_16_3To1_16_2;\nimport com.viaversion.viabackwards.protocol.v1_16_4to1_16_3.Protocol1_16_4To1_16_3;\nimport com.viaversion.viabackwards.protocol.v1_16to1_15_2.Protocol1_16To1_15_2;\nimport com.viaversion.viabackwards.protocol.v1_17_1to1_17.Protocol1_17_1To1_17;\nimport com.viaversion.viabackwards.protocol.v1_17to1_16_4.Protocol1_17To1_16_4;\nimport com.viaversion.viabackwards.protocol.v1_18_2to1_18.Protocol1_18_2To1_18;\nimport com.viaversion.viabackwards.protocol.v1_18to1_17_1.Protocol1_18To1_17_1;\nimport com.viaversion.viabackwards.protocol.v1_19_1to1_19.Protocol1_19_1To1_19;\nimport com.viaversion.viabackwards.protocol.v1_19_3to1_19_1.Protocol1_19_3To1_19_1;\nimport com.viaversion.viabackwards.protocol.v1_19_4to1_19_3.Protocol1_19_4To1_19_3;\nimport com.viaversion.viabackwards.protocol.v1_19to1_18_2.Protocol1_19To1_18_2;\nimport com.viaversion.viabackwards.protocol.v1_20_2to1_20.Protocol1_20_2To1_20;\nimport com.viaversion.viabackwards.protocol.v1_20_3to1_20_2.Protocol1_20_3To1_20_2;\nimport com.viaversion.viabackwards.protocol.v1_20_5to1_20_3.Protocol1_20_5To1_20_3;\nimport com.viaversion.viabackwards.protocol.v1_20to1_19_4.Protocol1_20To1_19_4;\nimport com.viaversion.viabackwards.protocol.v1_21_11to1_21_9.Protocol1_21_11To1_21_9;\nimport com.viaversion.viabackwards.protocol.v1_21_2to1_21.Protocol1_21_2To1_21;\nimport com.viaversion.viabackwards.protocol.v1_21_2to1_21.task.PlayerPacketsTickTask;\nimport com.viaversion.viabackwards.protocol.v1_21_4to1_21_2.Protocol1_21_4To1_21_2;\nimport com.viaversion.viabackwards.protocol.v1_21_5to1_21_4.Protocol1_21_5To1_21_4;\nimport com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.Protocol1_21_6To1_21_5;\nimport com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.task.ChestDialogViewTask;\nimport com.viaversion.viabackwards.protocol.v1_21_7to1_21_6.Protocol1_21_7To1_21_6;\nimport com.viaversion.viabackwards.protocol.v1_21_9to1_21_7.Protocol1_21_9To1_21_7;\nimport com.viaversion.viabackwards.protocol.v1_21to1_20_5.Protocol1_21To1_20_5;\nimport com.viaversion.viabackwards.protocol.v1_9_1to1_9.Protocol1_9_1To1_9;\nimport com.viaversion.viabackwards.protocol.v1_9_3to1_9_1.Protocol1_9_3To1_9_1;\nimport com.viaversion.viabackwards.protocol.v26_1to1_21_11.Protocol26_1To1_21_11;\nimport com.viaversion.viabackwards.utils.VersionInfo;\nimport com.viaversion.viaversion.api.Via;\nimport com.viaversion.viaversion.api.protocol.ProtocolManager;\nimport com.viaversion.viaversion.api.protocol.version.ProtocolVersion;\nimport com.viaversion.viaversion.update.Version;\nimport java.io.File;\nimport java.util.Arrays;\nimport java.util.logging.Logger;\n\npublic interface ViaBackwardsPlatform {\n\n    String MINIMUM_VV_VERSION = \"5.8.1\";\n\n    default void init(final File configFile) {\n        init(new ViaBackwardsConfig(configFile, getLogger()));\n    }\n\n    /**\n     * Initialize ViaBackwards.\n     */\n    default void init(final com.viaversion.viabackwards.api.ViaBackwardsConfig config) {\n        config.reload();\n        Via.getManager().getConfigurationProvider().register(config);\n\n        ViaBackwards.init(this, config);\n\n        if (isOutdated()) {\n            disable();\n            return;\n        }\n\n        Via.getManager().getSubPlatforms().add(VersionInfo.getImplementationVersion());\n\n        getLogger().info(\"Loading translations...\");\n        TranslatableMappings.loadTranslatables();\n\n        getLogger().info(\"Registering protocols...\");\n        BackwardsRegistrations.apply();\n\n        final ProtocolManager protocolManager = Via.getManager().getProtocolManager();\n        protocolManager.registerProtocol(new Protocol1_9_1To1_9(), ProtocolVersion.v1_9, ProtocolVersion.v1_9_1);\n        protocolManager.registerProtocol(new Protocol1_9_3To1_9_1(), Arrays.asList(ProtocolVersion.v1_9_1, ProtocolVersion.v1_9_2), ProtocolVersion.v1_9_3);\n        protocolManager.registerProtocol(new Protocol1_10To1_9_3(), ProtocolVersion.v1_9_3, ProtocolVersion.v1_10);\n\n        protocolManager.registerProtocol(new Protocol1_11To1_10(), ProtocolVersion.v1_10, ProtocolVersion.v1_11);\n        protocolManager.registerProtocol(new Protocol1_11_1To1_11(), ProtocolVersion.v1_11, ProtocolVersion.v1_11_1);\n\n        protocolManager.registerProtocol(new Protocol1_12To1_11_1(), ProtocolVersion.v1_11_1, ProtocolVersion.v1_12);\n        protocolManager.registerProtocol(new Protocol1_12_1To1_12(), ProtocolVersion.v1_12, ProtocolVersion.v1_12_1);\n        protocolManager.registerProtocol(new Protocol1_12_2To1_12_1(), ProtocolVersion.v1_12_1, ProtocolVersion.v1_12_2);\n\n        protocolManager.registerProtocol(new Protocol1_13To1_12_2(), ProtocolVersion.v1_12_2, ProtocolVersion.v1_13);\n        protocolManager.registerProtocol(new Protocol1_13_1To1_13(), ProtocolVersion.v1_13, ProtocolVersion.v1_13_1);\n        protocolManager.registerProtocol(new Protocol1_13_2To1_13_1(), ProtocolVersion.v1_13_1, ProtocolVersion.v1_13_2);\n\n        protocolManager.registerProtocol(new Protocol1_14To1_13_2(), ProtocolVersion.v1_13_2, ProtocolVersion.v1_14);\n        protocolManager.registerProtocol(new Protocol1_14_1To1_14(), ProtocolVersion.v1_14, ProtocolVersion.v1_14_1);\n        protocolManager.registerProtocol(new Protocol1_14_2To1_14_1(), ProtocolVersion.v1_14_1, ProtocolVersion.v1_14_2);\n        protocolManager.registerProtocol(new Protocol1_14_3To1_14_2(), ProtocolVersion.v1_14_2, ProtocolVersion.v1_14_3);\n        protocolManager.registerProtocol(new Protocol1_14_4To1_14_3(), ProtocolVersion.v1_14_3, ProtocolVersion.v1_14_4);\n\n        protocolManager.registerProtocol(new Protocol1_15To1_14_4(), ProtocolVersion.v1_14_4, ProtocolVersion.v1_15);\n        protocolManager.registerProtocol(new Protocol1_15_1To1_15(), ProtocolVersion.v1_15, ProtocolVersion.v1_15_1);\n        protocolManager.registerProtocol(new Protocol1_15_2To1_15_1(), ProtocolVersion.v1_15_1, ProtocolVersion.v1_15_2);\n\n        protocolManager.registerProtocol(new Protocol1_16To1_15_2(), ProtocolVersion.v1_15_2, ProtocolVersion.v1_16);\n        protocolManager.registerProtocol(new Protocol1_16_1To1_16(), ProtocolVersion.v1_16, ProtocolVersion.v1_16_1);\n        protocolManager.registerProtocol(new Protocol1_16_2To1_16_1(), ProtocolVersion.v1_16_1, ProtocolVersion.v1_16_2);\n        protocolManager.registerProtocol(new Protocol1_16_3To1_16_2(), ProtocolVersion.v1_16_2, ProtocolVersion.v1_16_3);\n        protocolManager.registerProtocol(new Protocol1_16_4To1_16_3(), ProtocolVersion.v1_16_3, ProtocolVersion.v1_16_4);\n\n        protocolManager.registerProtocol(new Protocol1_17To1_16_4(), ProtocolVersion.v1_16_4, ProtocolVersion.v1_17);\n        protocolManager.registerProtocol(new Protocol1_17_1To1_17(), ProtocolVersion.v1_17, ProtocolVersion.v1_17_1);\n\n        protocolManager.registerProtocol(new Protocol1_18To1_17_1(), ProtocolVersion.v1_17_1, ProtocolVersion.v1_18);\n        protocolManager.registerProtocol(new Protocol1_18_2To1_18(), ProtocolVersion.v1_18, ProtocolVersion.v1_18_2);\n\n        protocolManager.registerProtocol(new Protocol1_19To1_18_2(), ProtocolVersion.v1_18_2, ProtocolVersion.v1_19);\n        protocolManager.registerProtocol(new Protocol1_19_1To1_19(), ProtocolVersion.v1_19, ProtocolVersion.v1_19_1);\n        protocolManager.registerProtocol(new Protocol1_19_3To1_19_1(), ProtocolVersion.v1_19_1, ProtocolVersion.v1_19_3);\n        protocolManager.registerProtocol(new Protocol1_19_4To1_19_3(), ProtocolVersion.v1_19_3, ProtocolVersion.v1_19_4);\n\n        protocolManager.registerProtocol(new Protocol1_20To1_19_4(), ProtocolVersion.v1_19_4, ProtocolVersion.v1_20);\n        protocolManager.registerProtocol(new Protocol1_20_2To1_20(), ProtocolVersion.v1_20, ProtocolVersion.v1_20_2);\n        protocolManager.registerProtocol(new Protocol1_20_3To1_20_2(), ProtocolVersion.v1_20_2, ProtocolVersion.v1_20_3);\n        protocolManager.registerProtocol(new Protocol1_20_5To1_20_3(), ProtocolVersion.v1_20_3, ProtocolVersion.v1_20_5);\n\n        protocolManager.registerProtocol(new Protocol1_21To1_20_5(), ProtocolVersion.v1_20_5, ProtocolVersion.v1_21);\n        protocolManager.registerProtocol(new Protocol1_21_2To1_21(), ProtocolVersion.v1_21, ProtocolVersion.v1_21_2);\n        protocolManager.registerProtocol(new Protocol1_21_4To1_21_2(), ProtocolVersion.v1_21_2, ProtocolVersion.v1_21_4);\n        protocolManager.registerProtocol(new Protocol1_21_5To1_21_4(), ProtocolVersion.v1_21_4, ProtocolVersion.v1_21_5);\n\n        protocolManager.registerProtocol(new Protocol1_21_6To1_21_5(), ProtocolVersion.v1_21_5, ProtocolVersion.v1_21_6);\n        protocolManager.registerProtocol(new Protocol1_21_7To1_21_6(), ProtocolVersion.v1_21_6, ProtocolVersion.v1_21_7);\n        protocolManager.registerProtocol(new Protocol1_21_9To1_21_7(), ProtocolVersion.v1_21_7, ProtocolVersion.v1_21_9);\n        protocolManager.registerProtocol(new Protocol1_21_11To1_21_9(), ProtocolVersion.v1_21_9, ProtocolVersion.v1_21_11);\n\n        protocolManager.registerProtocol(new Protocol26_1To1_21_11(), ProtocolVersion.v1_21_11, ProtocolVersion.v26_1);\n    }\n\n    default void enable() {\n        final ProtocolVersion protocolVersion = Via.getAPI().getServerVersion().highestSupportedProtocolVersion();\n        if (protocolVersion.newerThanOrEqualTo(ProtocolVersion.v1_21_2)) {\n            Via.getPlatform().runRepeatingSync(new PlayerPacketsTickTask(), 1L);\n        }\n\n        if (protocolVersion.newerThanOrEqualTo(ProtocolVersion.v1_21_6)) {\n            Via.getPlatform().runRepeatingSync(new ChestDialogViewTask(), 20L);\n        }\n    }\n\n    /**\n     * Logger provided by the platform.\n     *\n     * @return logger instance\n     */\n    Logger getLogger();\n\n    default boolean isOutdated() {\n        String vvVersion = Via.getPlatform().getPluginVersion();\n        if (vvVersion != null && new Version(vvVersion).compareTo(new Version(MINIMUM_VV_VERSION + \"--\")) < 0) {\n            getLogger().severe(\"================================\");\n            getLogger().severe(\"YOUR VIAVERSION IS OUTDATED (you are running \" + vvVersion + \")\");\n            getLogger().severe(\"PLEASE USE VIAVERSION \" + MINIMUM_VV_VERSION + \" OR NEWER\");\n            getLogger().severe(\"LINK: https://ci.viaversion.com/\");\n            getLogger().severe(\"VIABACKWARDS WILL NOW DISABLE\");\n            getLogger().severe(\"================================\");\n            return true;\n        }\n        return false;\n    }\n\n    /**\n     * Disable the plugin.\n     */\n    void disable();\n\n    /**\n     * Returns ViaBackwards's data folder.\n     *\n     * @return data folder\n     */\n    File getDataFolder();\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/api/data/BackwardsMappingData.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.api.data;\n\nimport com.google.common.base.Preconditions;\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.NumberTag;\nimport com.viaversion.nbt.tag.StringTag;\nimport com.viaversion.nbt.tag.Tag;\nimport com.viaversion.viabackwards.ViaBackwards;\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viaversion.api.Via;\nimport com.viaversion.viaversion.api.data.BiMappings;\nimport com.viaversion.viaversion.api.data.IdentityMappings;\nimport com.viaversion.viaversion.api.data.MappingData;\nimport com.viaversion.viaversion.api.data.MappingDataBase;\nimport com.viaversion.viaversion.api.data.Mappings;\nimport com.viaversion.viaversion.api.protocol.Protocol;\nimport com.viaversion.viaversion.libs.fastutil.ints.Int2ObjectArrayMap;\nimport com.viaversion.viaversion.libs.fastutil.ints.Int2ObjectMap;\nimport com.viaversion.viaversion.libs.fastutil.ints.Int2ObjectOpenHashMap;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport org.checkerframework.checker.nullness.qual.Nullable;\n\npublic class BackwardsMappingData extends MappingDataBase {\n\n    private final Class<? extends Protocol<?, ?, ?, ?>> vvProtocolClass;\n    protected Int2ObjectMap<MappedItem> backwardsItemMappings;\n    private Map<String, String> entityNames;\n    private Int2ObjectMap<String> enchantmentNames;\n\n    public BackwardsMappingData(final String unmappedVersion, final String mappedVersion) {\n        this(unmappedVersion, mappedVersion, null);\n    }\n\n    public BackwardsMappingData(final String unmappedVersion, final String mappedVersion, @Nullable final Class<? extends Protocol<?, ?, ?, ?>> vvProtocolClass) {\n        super(unmappedVersion, mappedVersion);\n        Preconditions.checkArgument(vvProtocolClass == null || !BackwardsProtocol.class.isAssignableFrom(vvProtocolClass));\n        this.vvProtocolClass = vvProtocolClass;\n    }\n\n    @Override\n    protected void loadExtras(final CompoundTag data) {\n        final CompoundTag itemNames = data.getCompoundTag(\"itemnames\");\n        if (itemNames != null) {\n            Preconditions.checkNotNull(itemMappings);\n            backwardsItemMappings = new Int2ObjectOpenHashMap<>(itemNames.size());\n\n            final CompoundTag extraItemData = data.getCompoundTag(\"itemdata\");\n            for (final Map.Entry<String, Tag> entry : itemNames.entrySet()) {\n                final StringTag name = (StringTag) entry.getValue();\n                final int id = Integer.parseInt(entry.getKey());\n                Integer customModelData = null;\n                if (extraItemData != null && extraItemData.contains(entry.getKey())) {\n                    final CompoundTag entryTag = extraItemData.getCompoundTag(entry.getKey());\n                    final NumberTag customModelDataTag = entryTag.getNumberTag(\"custom_model_data\");\n                    customModelData = customModelDataTag != null ? customModelDataTag.asInt() : null;\n                }\n\n                backwardsItemMappings.put(id, new MappedItem(getNewItemId(id), name.getValue(), customModelData));\n            }\n        }\n\n        this.entityNames = loadNameByStringMappings(data, \"entitynames\");\n        this.enchantmentNames = loadNameByIdMappings(data, \"enchantmentnames\");\n    }\n\n    private @Nullable Map<String, String> loadNameByStringMappings(final CompoundTag data, final String key) {\n        final CompoundTag nameMappings = data.getCompoundTag(key);\n        if (nameMappings == null) {\n            return null;\n        }\n\n        final Map<String, String> map = new HashMap<>(nameMappings.size());\n        for (final Map.Entry<String, Tag> entry : nameMappings.entrySet()) {\n            final StringTag mappedTag = (StringTag) entry.getValue();\n            map.put(entry.getKey(), mappedTag.getValue());\n        }\n        return map;\n    }\n\n    private @Nullable Int2ObjectMap<String> loadNameByIdMappings(final CompoundTag data, final String key) {\n        final CompoundTag nameMappings = data.getCompoundTag(key);\n        if (nameMappings == null) {\n            return null;\n        }\n\n        final Int2ObjectMap<String> map = new Int2ObjectArrayMap<>(nameMappings.size());\n        for (final Map.Entry<String, Tag> entry : nameMappings.entrySet()) {\n            final StringTag mappedTag = (StringTag) entry.getValue();\n            map.put(Integer.parseInt(entry.getKey()), mappedTag.getValue());\n        }\n        return map;\n    }\n\n    @Override\n    protected @Nullable BiMappings loadBiMappings(final CompoundTag data, final String key) {\n        if (key.equals(\"items\") && vvProtocolClass != null) {\n            Mappings mappings = super.loadMappings(data, key);\n            final MappingData mappingData = Via.getManager().getProtocolManager().getProtocol(vvProtocolClass).getMappingData();\n            if (mappingData != null && mappingData.getItemMappings() != null) {\n                final BiMappings vvItemMappings = mappingData.getItemMappings();\n                if (mappings == null) {\n                    mappings = new IdentityMappings(vvItemMappings.mappedSize(), vvItemMappings.size());\n                }\n                return ItemMappings.of(mappings, vvItemMappings);\n            }\n        }\n        return super.loadBiMappings(data, key);\n    }\n\n    /**\n     * @see #getMappedItem(int) for custom backwards mappings\n     */\n    @Override\n    public int getNewItemId(final int id) {\n        // Don't warn on missing here\n        return this.itemMappings.getNewId(id);\n    }\n\n    @Override\n    public int getNewBlockId(final int id) {\n        // Don't warn on missing here\n        return this.blockMappings.getNewId(id);\n    }\n\n    @Override\n    public int getOldItemId(final int id) {\n        // Warn on missing\n        return checkValidity(id, this.itemMappings.inverse().getNewId(id), \"item\");\n    }\n\n    @Override\n    public int getNewAttributeId(final int id) {\n        return this.attributeMappings.getNewId(id);\n    }\n\n    public @Nullable MappedItem getMappedItem(final int id) {\n        return backwardsItemMappings != null ? backwardsItemMappings.get(id) : null;\n    }\n\n    public @Nullable String getMappedNamedSound(final String id) {\n        return getFullSoundMappings().mappedIdentifier(id);\n    }\n\n    public @Nullable String mappedEntityName(final String entityName) {\n        if (entityNames == null) {\n            getLogger().log(Level.SEVERE, \"No entity mappings found when requesting them for \" + entityName, new RuntimeException());\n            return null;\n        }\n        return entityNames.get(entityName);\n    }\n\n    public @Nullable String mappedEnchantmentName(final int enchantmentId) {\n        if (enchantmentNames == null) {\n            ViaBackwards.getPlatform().getLogger().log(Level.SEVERE, \"No enchantment name mappings found when requesting \" + enchantmentId, new RuntimeException());\n            return null;\n        }\n        return enchantmentNames.get(enchantmentId);\n    }\n\n    public @Nullable Int2ObjectMap<MappedItem> getBackwardsItemMappings() {\n        return backwardsItemMappings;\n    }\n\n    public @Nullable Class<? extends Protocol<?, ?, ?, ?>> getViaVersionProtocolClass() {\n        return vvProtocolClass;\n    }\n\n    @Override\n    protected Logger getLogger() {\n        return ViaBackwards.getPlatform().getLogger();\n    }\n\n    @Override\n    protected @Nullable CompoundTag readMappingsFile(final String name) {\n        return BackwardsMappingDataLoader.INSTANCE.loadNBTFromDir(name);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/api/data/BackwardsMappingDataLoader.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.api.data;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.Tag;\nimport com.viaversion.viabackwards.ViaBackwards;\nimport com.viaversion.viaversion.api.data.MappingDataLoader;\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.Map;\nimport java.util.logging.Logger;\nimport org.checkerframework.checker.nullness.qual.Nullable;\n\npublic class BackwardsMappingDataLoader extends MappingDataLoader {\n\n    public static final BackwardsMappingDataLoader INSTANCE = new BackwardsMappingDataLoader(BackwardsMappingDataLoader.class, \"assets/viabackwards/data/\");\n\n    public BackwardsMappingDataLoader(final Class<?> dataLoaderClass, final String dataPath) {\n        super(dataLoaderClass, dataPath);\n    }\n\n    /**\n     * Returns nbt data from the plugin folder or packed assets.\n     * If a file with the same name exists in the plugin folder, the data of the original packed tag will be merged with the file's tag.\n     *\n     * @param name name of the file\n     * @return nbt data from the plugin folder or packed assets\n     */\n    public @Nullable CompoundTag loadNBTFromDir(final String name) {\n        final CompoundTag packedData = loadNBT(name);\n\n        final File file = new File(getDataFolder(), name);\n        if (!file.exists()) {\n            return packedData;\n        }\n\n        getLogger().info(\"Loading \" + name + \" from plugin folder\");\n        try {\n            final CompoundTag fileData = MAPPINGS_READER.read(file.toPath(), false);\n            return mergeTags(packedData, fileData);\n        } catch (final IOException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    private CompoundTag mergeTags(final CompoundTag original, final CompoundTag extra) {\n        for (final Map.Entry<String, Tag> entry : extra.entrySet()) {\n            if (entry.getValue() instanceof CompoundTag) {\n                // For compound tags, don't replace the entire tag\n                final CompoundTag originalEntry = original.getCompoundTag(entry.getKey());\n                if (originalEntry != null) {\n                    mergeTags(originalEntry, (CompoundTag) entry.getValue());\n                    continue;\n                }\n            }\n\n            original.put(entry.getKey(), entry.getValue());\n        }\n        return original;\n    }\n\n    @Override\n    public Logger getLogger() {\n        return ViaBackwards.getPlatform().getLogger();\n    }\n\n    @Override\n    public File getDataFolder() {\n        return ViaBackwards.getPlatform().getDataFolder();\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/api/data/ItemMappings.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.api.data;\n\nimport com.viaversion.viaversion.api.data.BiMappingsBase;\nimport com.viaversion.viaversion.api.data.Mappings;\n\npublic final class ItemMappings extends BiMappingsBase {\n\n    private ItemMappings(final Mappings mappings, final Mappings inverse) {\n        super(mappings, inverse);\n    }\n\n    public static ItemMappings of(final Mappings mappings, final Mappings inverse) {\n        return new ItemMappings(mappings, inverse);\n    }\n\n    @Override\n    public void setNewId(final int id, final int mappedId) {\n        // Only set one-way\n        mappings.setNewId(id, mappedId);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/api/data/MappedItem.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.api.data;\n\nimport com.viaversion.nbt.tag.Tag;\nimport com.viaversion.viaversion.util.ComponentUtil;\nimport org.checkerframework.checker.nullness.qual.Nullable;\n\npublic class MappedItem {\n\n    private final int id;\n    private final String jsonName;\n    private final Tag tagName;\n    private final Integer customModelData;\n\n    public MappedItem(final int id, final String name) {\n        this(id, name, null);\n    }\n\n    public MappedItem(final int id, final String name, @Nullable final Integer customModelData) {\n        this.id = id;\n        this.jsonName = ComponentUtil.legacyToJsonString(\"§f\" + name, true);\n        this.tagName = ComponentUtil.jsonStringToTag(jsonName);\n        this.customModelData = customModelData;\n    }\n\n    public int id() {\n        return id;\n    }\n\n    public String jsonName() {\n        return jsonName;\n    }\n\n    public Tag tagName() {\n        return tagName;\n    }\n\n    public @Nullable Integer customModelData() {\n        return customModelData;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/api/data/MappedLegacyBlockItem.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.api.data;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.viaversion.util.IdAndData;\nimport org.checkerframework.checker.nullness.qual.Nullable;\n\npublic class MappedLegacyBlockItem {\n\n    private final int id;\n    private final short data;\n    private final String name;\n    private final IdAndData block;\n    private BlockEntityHandler blockEntityHandler;\n\n    public MappedLegacyBlockItem(int id) {\n        this(id, (short) -1, null, Type.ITEM);\n    }\n\n    public MappedLegacyBlockItem(int id, short data, @Nullable String name, Type type) {\n        this.id = id;\n        this.data = data;\n        this.name = name != null ? \"§f\" + name : null;\n        this.block = type != Type.ITEM ? data != -1 ? new IdAndData(id, data) : new IdAndData(id) : null;\n    }\n\n    public int getId() {\n        return id;\n    }\n\n    public short getData() {\n        return data;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public IdAndData getBlock() {\n        return block;\n    }\n\n    public boolean hasBlockEntityHandler() {\n        return blockEntityHandler != null;\n    }\n\n    public @Nullable BlockEntityHandler getBlockEntityHandler() {\n        return blockEntityHandler;\n    }\n\n    public void setBlockEntityHandler(@Nullable BlockEntityHandler blockEntityHandler) {\n        this.blockEntityHandler = blockEntityHandler;\n    }\n\n    @FunctionalInterface\n    public interface BlockEntityHandler {\n\n        void handleCompoundTag(int block, CompoundTag tag);\n    }\n\n    public enum Type {\n\n        ITEM(\"items\"),\n        BLOCK_ITEM(\"block-items\"),\n        BLOCK(\"blocks\");\n\n        final String name;\n\n        Type(final String name) {\n            this.name = name;\n        }\n\n        public String getName() {\n            return name;\n        }\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/api/data/TranslatableMappings.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.api.data;\n\nimport com.viaversion.viabackwards.ViaBackwards;\nimport com.viaversion.viaversion.api.protocol.Protocol;\nimport com.viaversion.viaversion.libs.gson.JsonElement;\nimport com.viaversion.viaversion.libs.gson.JsonObject;\nimport java.util.HashMap;\nimport java.util.Map;\nimport org.checkerframework.checker.nullness.qual.Nullable;\n\npublic final class TranslatableMappings {\n\n    private static final Map<String, Map<String, String>> TRANSLATABLES = new HashMap<>();\n\n    public static void loadTranslatables() {\n        if (!TRANSLATABLES.isEmpty()) {\n            throw new IllegalStateException(\"Translatables already loaded!\");\n        }\n        fillTranslatables(BackwardsMappingDataLoader.INSTANCE.loadFromDataDir(\"translation-mappings.json\"), TRANSLATABLES);\n    }\n\n    public static void fillTranslatables(final JsonObject jsonObject, final Map<String, Map<String, String>> translatables) {\n        for (final Map.Entry<String, JsonElement> entry : jsonObject.entrySet()) {\n            final Map<String, String> versionMappings = new HashMap<>();\n            translatables.put(entry.getKey(), versionMappings);\n            for (final Map.Entry<String, JsonElement> translationEntry : entry.getValue().getAsJsonObject().entrySet()) {\n                versionMappings.put(translationEntry.getKey(), translationEntry.getValue().getAsString());\n            }\n        }\n    }\n\n    public static Map<String, String> translatablesFor(final Protocol<?, ?, ?, ?> protocol) {\n        final String version = protocol.getClass().getSimpleName()\n            .replace(\"Protocol\", \"\")\n            .split(\"To\")[0]\n            .replace(\"_\", \".\");\n        return translatablesFor(version);\n    }\n\n    public static Map<String, String> translatablesFor(final String version) {\n        final Map<String, String> translatableMappings = getTranslatableMappings(version);\n        if (translatableMappings == null) {\n            ViaBackwards.getPlatform().getLogger().warning(\"Missing \" + version + \" translatables!\");\n            return new HashMap<>();\n        }\n        return translatableMappings;\n    }\n\n    public static @Nullable Map<String, String> getTranslatableMappings(final String sectionIdentifier) {\n        return TRANSLATABLES.get(sectionIdentifier);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/api/entities/storage/EntityObjectData.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.api.entities.storage;\n\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\n\npublic class EntityObjectData extends EntityReplacement {\n    private final int objectData;\n\n    public EntityObjectData(BackwardsProtocol<?, ?, ?, ?> protocol, String key, int id, int replacementId, int objectData) {\n        super(protocol, key, id, replacementId);\n        this.objectData = objectData;\n    }\n\n    @Override\n    public boolean isObjectType() {\n        return true;\n    }\n\n    @Override\n    public int objectData() {\n        return objectData;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/api/entities/storage/EntityPositionHandler.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.api.entities.storage;\n\nimport com.viaversion.viabackwards.api.rewriters.EntityRewriterBase;\nimport com.viaversion.viaversion.api.Via;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.data.entity.StoredEntityData;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.util.ProtocolLogger;\nimport java.util.function.Supplier;\n\npublic class EntityPositionHandler {\n\n    public static final double RELATIVE_MOVE_FACTOR = 32 * 128;\n    private final EntityRewriterBase<?, ?> entityRewriter;\n    private final Class<? extends EntityPositionStorage> storageClass;\n    private final Supplier<? extends EntityPositionStorage> storageSupplier;\n    private boolean warnedForMissingEntity;\n\n    public EntityPositionHandler(EntityRewriterBase<?, ?> entityRewriter,\n                                 Class<? extends EntityPositionStorage> storageClass, Supplier<? extends EntityPositionStorage> storageSupplier) {\n        this.entityRewriter = entityRewriter;\n        this.storageClass = storageClass;\n        this.storageSupplier = storageSupplier;\n    }\n\n    public void cacheEntityPosition(PacketWrapper wrapper, boolean create, boolean relative) {\n        cacheEntityPosition(wrapper,\n            wrapper.get(Types.DOUBLE, 0), wrapper.get(Types.DOUBLE, 1), wrapper.get(Types.DOUBLE, 2), create, relative);\n    }\n\n    public void cacheEntityPosition(PacketWrapper wrapper, double x, double y, double z, boolean create, boolean relative) {\n        cacheEntityPosition(wrapper, wrapper.get(Types.VAR_INT, 0), x, y, z, create, relative);\n    }\n\n    public void cacheEntityPosition(PacketWrapper wrapper, int entityId, double x, double y, double z, boolean create, boolean relative) {\n        StoredEntityData storedEntity = entityRewriter.tracker(wrapper.user()).entityData(entityId);\n        if (storedEntity == null) {\n            if (Via.getManager().isDebug()) { // There is too many plugins violating this out there, and reading seems to be hard! :>\n                ProtocolLogger logger = entityRewriter.protocol().getLogger();\n                logger.warning(\"Stored entity with id \" + entityId + \" missing at position: \" + x + \" - \" + y + \" - \" + z + \" in \" + storageClass.getSimpleName());\n                if (entityId == -1 && x == 0 && y == 0 && z == 0) {\n                    logger.warning(\"DO NOT REPORT THIS TO VIA, THIS IS A PLUGIN ISSUE\");\n                } else if (!warnedForMissingEntity) {\n                    warnedForMissingEntity = true;\n                    logger.warning(\"This is very likely caused by a plugin sending a teleport packet for an entity outside of the player's range.\");\n                }\n            }\n            return;\n        }\n\n        EntityPositionStorage positionStorage;\n        if (create) {\n            positionStorage = storageSupplier.get();\n            storedEntity.put(positionStorage);\n        } else {\n            positionStorage = storedEntity.get(storageClass);\n            if (positionStorage == null) {\n                entityRewriter.protocol().getLogger().warning(\"Stored entity with id \" + entityId + \" missing \" + storageClass.getSimpleName());\n                return;\n            }\n        }\n\n        if (relative) {\n            positionStorage.addRelativePosition(x, y, z);\n        } else {\n            positionStorage.setPosition(x, y, z);\n        }\n    }\n\n    public EntityPositionStorage getStorage(UserConnection user, int entityId) {\n        StoredEntityData storedEntity = entityRewriter.tracker(user).entityData(entityId);\n        EntityPositionStorage entityStorage;\n        if (storedEntity == null || (entityStorage = storedEntity.get(EntityPositionStorage.class)) == null) {\n            entityRewriter.protocol().getLogger().warning(\"Untracked entity with id \" + entityId + \" in \" + storageClass.getSimpleName());\n            return null;\n        }\n        return entityStorage;\n    }\n\n    public static void writeFacingAngles(PacketWrapper wrapper, double x, double y, double z, double targetX, double targetY, double targetZ) {\n        double dX = targetX - x;\n        double dY = targetY - y;\n        double dZ = targetZ - z;\n        double r = Math.sqrt(dX * dX + dY * dY + dZ * dZ);\n        double yaw = -Math.atan2(dX, dZ) / Math.PI * 180;\n        if (yaw < 0) {\n            yaw = 360 + yaw;\n        }\n        double pitch = -Math.asin(dY / r) / Math.PI * 180;\n\n        wrapper.write(Types.BYTE, (byte) (yaw * 256f / 360f));\n        wrapper.write(Types.BYTE, (byte) (pitch * 256f / 360f));\n    }\n\n    public static void writeFacingDegrees(PacketWrapper wrapper, double x, double y, double z, double targetX, double targetY, double targetZ) {\n        double dX = targetX - x;\n        double dY = targetY - y;\n        double dZ = targetZ - z;\n        double r = Math.sqrt(dX * dX + dY * dY + dZ * dZ);\n        double yaw = -Math.atan2(dX, dZ) / Math.PI * 180;\n        if (yaw < 0) {\n            yaw = 360 + yaw;\n        }\n        double pitch = -Math.asin(dY / r) / Math.PI * 180;\n\n        wrapper.write(Types.FLOAT, (float) yaw);\n        wrapper.write(Types.FLOAT, (float) pitch);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/api/entities/storage/EntityPositionStorage.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.api.entities.storage;\n\npublic abstract class EntityPositionStorage {\n    private double x;\n    private double y;\n    private double z;\n\n    public double x() {\n        return x;\n    }\n\n    public double y() {\n        return y;\n    }\n\n    public double z() {\n        return z;\n    }\n\n    public void setPosition(double x, double y, double z) {\n        this.x = x;\n        this.y = y;\n        this.z = z;\n    }\n\n    public void addRelativePosition(double relX, double relY, double relZ) {\n        this.x += relX;\n        this.y += relY;\n        this.z += relZ;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/api/entities/storage/EntityReplacement.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.viaversion.viabackwards.api.entities.storage;\n\nimport com.viaversion.nbt.tag.StringTag;\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityType;\nimport com.viaversion.viaversion.util.ComponentUtil;\nimport java.util.Locale;\nimport org.checkerframework.checker.nullness.qual.Nullable;\n\npublic class EntityReplacement {\n    private final BackwardsProtocol<?, ?, ?, ?> protocol;\n    private final int id;\n    private final int replacementId;\n    private final String key;\n    private ComponentType componentType = ComponentType.NONE;\n    private EntityDataCreator defaultData;\n\n    public EntityReplacement(BackwardsProtocol<?, ?, ?, ?> protocol, EntityType type, int replacementId) {\n        this(protocol, type.name(), type.getId(), replacementId);\n    }\n\n    public EntityReplacement(BackwardsProtocol<?, ?, ?, ?> protocol, String key, int id, int replacementId) {\n        this.protocol = protocol;\n        this.id = id;\n        this.replacementId = replacementId;\n        this.key = key.toLowerCase(Locale.ROOT);\n    }\n\n    public EntityReplacement jsonName() {\n        this.componentType = ComponentType.JSON;\n        return this;\n    }\n\n    public EntityReplacement tagName() {\n        this.componentType = ComponentType.TAG;\n        return this;\n    }\n\n    public EntityReplacement plainName() {\n        this.componentType = ComponentType.PLAIN;\n        return this;\n    }\n\n    public EntityReplacement spawnEntityData(EntityDataCreator handler) {\n        this.defaultData = handler;\n        return this;\n    }\n\n    public boolean hasBaseData() {\n        return this.defaultData != null;\n    }\n\n    public int typeId() {\n        return id;\n    }\n\n    /**\n     * @return custom mobname, can be either a String or a JsonElement\n     */\n    public @Nullable Object entityName() {\n        if (componentType == ComponentType.NONE) {\n            return null;\n        }\n\n        final String name = protocol.getMappingData().mappedEntityName(key);\n        if (name == null) {\n            return null;\n        }\n\n        if (componentType == ComponentType.JSON) {\n            return ComponentUtil.legacyToJson(name);\n        } else if (componentType == ComponentType.TAG) {\n            return new StringTag(name);\n        }\n        return name;\n    }\n\n    public int replacementId() {\n        return replacementId;\n    }\n\n    public @Nullable EntityDataCreator defaultData() {\n        return defaultData;\n    }\n\n    public boolean isObjectType() {\n        return false;\n    }\n\n    public int objectData() {\n        return -1;\n    }\n\n    @Override\n    public String toString() {\n        return \"EntityReplacement{\" +\n            \"protocol=\" + protocol +\n            \", id=\" + id +\n            \", replacementId=\" + replacementId +\n            \", key='\" + key + '\\'' +\n            \", componentType=\" + componentType +\n            \", defaultData=\" + defaultData +\n            '}';\n    }\n\n    @FunctionalInterface\n    public interface EntityDataCreator {\n\n        void createData(WrappedEntityData storage);\n    }\n\n    private enum ComponentType {\n        PLAIN,\n        JSON,\n        TAG,\n        NONE\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/api/entities/storage/PlayerPositionStorage.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.api.entities.storage;\n\nimport com.viaversion.viaversion.api.connection.StorableObject;\n\npublic abstract class PlayerPositionStorage implements StorableObject {\n    private double x;\n    private double y;\n    private double z;\n\n    protected PlayerPositionStorage() {\n    }\n\n    public double x() {\n        return x;\n    }\n\n    public double y() {\n        return y;\n    }\n\n    public double z() {\n        return z;\n    }\n\n    public void setX(final double x) {\n        this.x = x;\n    }\n\n    public void setY(final double y) {\n        this.y = y;\n    }\n\n    public void setZ(final double z) {\n        this.z = z;\n    }\n\n    public void setPosition(double x, double y, double z) {\n        this.x = x;\n        this.y = y;\n        this.z = z;\n    }\n\n    public void addRelativePosition(double relX, double relY, double relZ) {\n        this.x += relX;\n        this.y += relY;\n        this.z += relZ;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/api/entities/storage/WrappedEntityData.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.viaversion.viabackwards.api.entities.storage;\n\nimport com.viaversion.viaversion.api.minecraft.entitydata.EntityData;\nimport java.util.List;\nimport org.checkerframework.checker.nullness.qual.Nullable;\n\npublic record WrappedEntityData(List<EntityData> entityDataList) {\n\n    public boolean has(EntityData data) {\n        return this.entityDataList.contains(data);\n    }\n\n    public void remove(EntityData data) {\n        this.entityDataList.remove(data);\n    }\n\n    public void remove(int index) {\n        entityDataList.removeIf(data -> data.id() == index);\n    }\n\n    public void add(EntityData data) {\n        this.entityDataList.add(data);\n    }\n\n    public @Nullable EntityData get(int index) {\n        for (EntityData data : this.entityDataList) {\n            if (index == data.id()) {\n                return data;\n            }\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/api/rewriters/BackwardsItemRewriter.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.api.rewriters;\n\nimport com.viaversion.nbt.tag.ByteTag;\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.IntTag;\nimport com.viaversion.nbt.tag.ListTag;\nimport com.viaversion.nbt.tag.NumberTag;\nimport com.viaversion.nbt.tag.StringTag;\nimport com.viaversion.nbt.tag.Tag;\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viabackwards.api.data.MappedItem;\nimport com.viaversion.viabackwards.item.DataItemWithExtras;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.item.Item;\nimport com.viaversion.viaversion.api.protocol.packet.ClientboundPacketType;\nimport com.viaversion.viaversion.api.protocol.packet.ServerboundPacketType;\nimport com.viaversion.viaversion.api.type.Type;\nimport com.viaversion.viaversion.libs.gson.JsonElement;\nimport java.util.List;\nimport org.checkerframework.checker.nullness.qual.Nullable;\n\npublic class BackwardsItemRewriter<C extends ClientboundPacketType, S extends ServerboundPacketType,\n    T extends BackwardsProtocol<C, ?, ?, S>> extends BackwardsItemRewriterBase<C, S, T> {\n\n    public BackwardsItemRewriter(T protocol, Type<Item> itemType, Type<Item[]> itemArrayType) {\n        super(protocol, itemType, itemArrayType, true);\n    }\n\n    public BackwardsItemRewriter(T protocol, Type<Item> itemType, Type<Item[]> itemArrayType, Type<Item> mappedItemType, Type<Item[]> mappedItemArrayType) {\n        super(protocol, itemType, itemArrayType, mappedItemType, mappedItemArrayType, true);\n    }\n\n    @Override\n    public @Nullable Item handleItemToClient(UserConnection connection, @Nullable Item item) {\n        if (item == null) {\n            return null;\n        }\n\n        CompoundTag display = item.tag() != null ? item.tag().getCompoundTag(\"display\") : null;\n        if (protocol.getComponentRewriter() != null && display != null) {\n            final DataItemWithExtras fullItem;\n            if (item instanceof DataItemWithExtras) {\n                fullItem = (DataItemWithExtras) item;\n            } else {\n                item = fullItem = new DataItemWithExtras(item);\n            }\n\n            // Handle name and lore components\n            final JsonElement name = fullItem.name();\n            if (name != null) {\n                final JsonElement updatedName = name.deepCopy();\n                protocol.getComponentRewriter().processText(connection, updatedName);\n                if (!updatedName.equals(name)) {\n                    final StringTag rawName = fullItem.rawName();\n                    saveStringTag(display, rawName, \"Name\");\n                    rawName.setValue(updatedName.toString());\n                }\n            }\n\n            final List<JsonElement> lore = fullItem.lore();\n            if (lore != null) {\n                boolean changed = false;\n                final ListTag<StringTag> rawLore = fullItem.rawLore();\n                for (int i = 0; i < lore.size(); i++) {\n                    final JsonElement loreEntry = lore.get(i);\n                    final JsonElement updatedLoreEntry = loreEntry.deepCopy();\n                    protocol.getComponentRewriter().processText(connection, updatedLoreEntry);\n                    if (updatedLoreEntry.equals(loreEntry)) {\n                        continue;\n                    }\n\n                    if (!changed) {\n                        // Backup original lore before doing any modifications\n                        changed = true;\n                        saveListTag(display, rawLore, \"Lore\");\n                    }\n\n                    final StringTag rawLoreEntry = rawLore.get(i);\n                    rawLoreEntry.setValue(updatedLoreEntry.toString());\n                }\n            }\n        }\n\n        MappedItem data = protocol.getMappingData() != null ? protocol.getMappingData().getMappedItem(item.identifier()) : null;\n        if (data == null) {\n            // Just rewrite the id\n            return super.handleItemToClient(connection, item);\n        }\n\n        if (item.tag() == null) {\n            item.setTag(new CompoundTag());\n        }\n\n        // Save original id, set remapped id\n        item.tag().putInt(nbtTagName(\"id\"), item.identifier());\n        item.setIdentifier(data.id());\n\n        // Add custom model data\n        if (data.customModelData() != null && !item.tag().contains(\"CustomModelData\")) {\n            item.tag().putInt(\"CustomModelData\", data.customModelData());\n        }\n\n        // Set custom name - only done if there is no original one\n        if (display == null) {\n            item.tag().put(\"display\", display = new CompoundTag());\n        }\n        if (!display.contains(\"Name\")) {\n            display.put(\"Name\", new StringTag(data.jsonName()));\n            display.put(nbtTagName(\"customName\"), new ByteTag(false));\n        }\n        return item;\n    }\n\n    @Override\n    public @Nullable Item handleItemToServer(UserConnection connection, @Nullable Item item) {\n        if (item == null) return null;\n\n        item = super.handleItemToServer(connection, item);\n        if (item.tag() != null) {\n            Tag originalId = item.tag().remove(nbtTagName(\"id\"));\n            if (originalId instanceof IntTag) {\n                item.setIdentifier(((NumberTag) originalId).asInt());\n            }\n        }\n        return item;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/api/rewriters/BackwardsItemRewriterBase.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.api.rewriters;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.ListTag;\nimport com.viaversion.nbt.tag.StringTag;\nimport com.viaversion.nbt.tag.Tag;\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.item.Item;\nimport com.viaversion.viaversion.api.protocol.packet.ClientboundPacketType;\nimport com.viaversion.viaversion.api.protocol.packet.ServerboundPacketType;\nimport com.viaversion.viaversion.api.type.Type;\nimport com.viaversion.viaversion.rewriter.ItemRewriter;\nimport org.checkerframework.checker.nullness.qual.Nullable;\n\npublic abstract class BackwardsItemRewriterBase<C extends ClientboundPacketType, S extends ServerboundPacketType,\n    T extends BackwardsProtocol<C, ?, ?, S>> extends ItemRewriter<C, S, T> {\n\n    protected final boolean jsonNameFormat;\n\n    protected BackwardsItemRewriterBase(T protocol, Type<Item> itemType, Type<Item[]> itemArrayType, Type<Item> mappedItemType, Type<Item[]> mappedItemArrayType, boolean jsonFormat) {\n        super(protocol, itemType, itemArrayType, mappedItemType, mappedItemArrayType);\n        this.jsonNameFormat = jsonFormat;\n    }\n\n    protected BackwardsItemRewriterBase(T protocol, Type<Item> itemType, Type<Item[]> itemArrayType, boolean jsonNameFormat) {\n        this(protocol, itemType, itemArrayType, itemType, itemArrayType, jsonNameFormat);\n    }\n\n    @Override\n    public @Nullable Item handleItemToServer(UserConnection connection, @Nullable Item item) {\n        if (item == null) return null;\n        item = super.handleItemToServer(connection, item);\n\n        restoreDisplayTag(item);\n        return item;\n    }\n\n    protected boolean hasBackupTag(CompoundTag tag, String tagName) {\n        return tag.contains(nbtTagName(tagName));\n    }\n\n    protected void saveStringTag(CompoundTag tag, StringTag original, String name) {\n        // Multiple places might try to backup data\n        String backupName = nbtTagName(name);\n        if (!tag.contains(backupName)) {\n            tag.putString(backupName, original.getValue());\n        }\n    }\n\n    protected void saveListTag(CompoundTag tag, ListTag<?> original, String name) {\n        // Multiple places might try to backup data\n        String backupName = nbtTagName(name);\n        if (!tag.contains(backupName)) {\n            tag.put(backupName, original.copy());\n        }\n    }\n\n    protected void restoreDisplayTag(Item item) {\n        if (item.tag() == null) return;\n\n        CompoundTag display = item.tag().getCompoundTag(\"display\");\n        if (display != null) {\n            // Remove custom name / restore original name\n            if (display.remove(nbtTagName(\"customName\")) != null) {\n                display.remove(\"Name\");\n            } else {\n                restoreStringTag(display, \"Name\");\n            }\n\n            // Restore lore\n            restoreListTag(display, \"Lore\");\n        }\n    }\n\n    protected void restoreStringTag(CompoundTag tag, String tagName) {\n        Tag original = tag.remove(nbtTagName(tagName));\n        if (original instanceof StringTag) {\n            tag.putString(tagName, ((StringTag) original).getValue());\n        }\n    }\n\n    protected void restoreListTag(CompoundTag tag, String tagName) {\n        Tag original = tag.remove(nbtTagName(tagName));\n        if (original instanceof ListTag) {\n            tag.put(tagName, ((ListTag<?>) original).copy());\n        }\n    }\n\n    @Override\n    public String nbtTagName() {\n        return \"VB|\" + protocol.getClass().getSimpleName();\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/api/rewriters/BackwardsRegistryRewriter.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.api.rewriters;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.RegistryEntry;\nimport com.viaversion.viaversion.rewriter.RegistryDataRewriter;\nimport com.viaversion.viaversion.util.Key;\n\npublic class BackwardsRegistryRewriter extends RegistryDataRewriter {\n\n    private final BackwardsProtocol<?, ?, ?, ?> protocol;\n\n    public BackwardsRegistryRewriter(final BackwardsProtocol<?, ?, ?, ?> protocol) {\n        super(protocol);\n        this.protocol = protocol;\n    }\n\n    @Override\n    public RegistryEntry[] handle(final UserConnection connection, final String key, final RegistryEntry[] entries) {\n        if (Key.stripMinecraftNamespace(key).equals(\"worldgen/biome\")) {\n            for (final RegistryEntry entry : entries) {\n                final CompoundTag biome = (CompoundTag) entry.tag();\n                if (biome == null) {\n                    continue;\n                }\n\n                final CompoundTag effects = biome.getCompoundTag(\"effects\");\n                updateBiomeEffects(effects);\n            }\n        }\n        return super.handle(connection, key, entries);\n    }\n\n    @Override\n    public void updateJukeboxSongs(final RegistryEntry[] entries) {\n        for (final RegistryEntry entry : entries) {\n            if (entry.tag() == null) {\n                continue;\n            }\n\n            updateSound((CompoundTag) entry.tag(), \"sound_event\");\n        }\n    }\n\n    private void updateBiomeEffects(final CompoundTag effects) {\n        updateSound(effects.getCompoundTag(\"mood_sound\"), \"sound\");\n        updateSound(effects.getCompoundTag(\"additions_sound\"), \"sound\");\n        updateSound(effects.getCompoundTag(\"music\"), \"sound\");\n        updateSound(effects, \"ambient_sound\");\n    }\n\n    private void updateSound(final CompoundTag tag, final String name) {\n        if (tag == null) {\n            return;\n        }\n\n        final String sound = tag.getString(name);\n        if (sound == null) {\n            return;\n        }\n\n        final String mappedSound = protocol.getMappingData().getMappedNamedSound(sound);\n        if (mappedSound == null) {\n            return;\n        }\n\n        if (mappedSound.isEmpty()) {\n            tag.putString(name, \"minecraft:intentionally_empty\");\n        } else {\n            tag.putString(name, mappedSound);\n        }\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/api/rewriters/BackwardsStructuredItemRewriter.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.api.rewriters;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.FloatTag;\nimport com.viaversion.nbt.tag.IntArrayTag;\nimport com.viaversion.nbt.tag.IntTag;\nimport com.viaversion.nbt.tag.ListTag;\nimport com.viaversion.nbt.tag.StringTag;\nimport com.viaversion.nbt.tag.Tag;\nimport com.viaversion.viabackwards.ViaBackwards;\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viabackwards.api.data.BackwardsMappingData;\nimport com.viaversion.viabackwards.api.data.MappedItem;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.EitherHolder;\nimport com.viaversion.viaversion.api.minecraft.Holder;\nimport com.viaversion.viaversion.api.minecraft.HolderSet;\nimport com.viaversion.viaversion.api.minecraft.SoundEvent;\nimport com.viaversion.viaversion.api.minecraft.data.StructuredDataContainer;\nimport com.viaversion.viaversion.api.minecraft.data.StructuredDataKey;\nimport com.viaversion.viaversion.api.minecraft.item.Item;\nimport com.viaversion.viaversion.api.minecraft.item.data.CustomModelData1_21_4;\nimport com.viaversion.viaversion.api.protocol.packet.ClientboundPacketType;\nimport com.viaversion.viaversion.api.protocol.packet.ServerboundPacketType;\nimport com.viaversion.viaversion.api.protocol.version.ProtocolVersion;\nimport com.viaversion.viaversion.rewriter.StructuredItemRewriter;\nimport com.viaversion.viaversion.util.ArrayUtil;\nimport com.viaversion.viaversion.util.Key;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.function.BiConsumer;\nimport java.util.function.Function;\nimport org.checkerframework.checker.nullness.qual.Nullable;\n\npublic class BackwardsStructuredItemRewriter<C extends ClientboundPacketType, S extends ServerboundPacketType,\n    T extends BackwardsProtocol<C, ?, ?, S>> extends StructuredItemRewriter<C, S, T> {\n\n    private static final int[] EMPTY_INT_ARRAY = new int[0];\n    private static final String GLOBAL_MODEL_DATA_MARKER = \"VB|injected_cmd\";\n\n    private final String nbtTagName;\n\n    public BackwardsStructuredItemRewriter(T protocol) {\n        super(protocol);\n        this.nbtTagName = \"VB|\" + protocol.getClass().getSimpleName();\n    }\n\n    @Override\n    protected void backupInconvertibleData(final UserConnection connection, final Item item, final StructuredDataContainer dataContainer, final CompoundTag backupTag) {\n        super.backupInconvertibleData(connection, item, dataContainer, backupTag);\n\n        final BackwardsMappingData mappingData = protocol.getMappingData();\n        final MappedItem mappedItem = mappingData != null ? mappingData.getMappedItem(item.identifier()) : null;\n        if (mappedItem == null) {\n            return;\n        }\n\n        final CompoundTag customTag = createCustomTag(item);\n        customTag.putInt(nbtTagName(\"id\"), item.identifier()); // Save original id\n\n        // Add custom model data\n        final boolean addOriginalIdentifier = ViaBackwards.getConfig().passOriginalItemNameToResourcePacks();\n        if (mappedItem.customModelData() != null || addOriginalIdentifier) {\n            if (connection.getProtocolInfo().protocolVersion().newerThanOrEqualTo(ProtocolVersion.v1_21_4)) {\n                addCustomModelData(item, addOriginalIdentifier, mappedItem, customTag);\n            } else if (mappedItem.customModelData() != null && !dataContainer.has(StructuredDataKey.CUSTOM_MODEL_DATA1_20_5)) {\n                dataContainer.set(StructuredDataKey.CUSTOM_MODEL_DATA1_20_5, mappedItem.customModelData());\n            }\n        }\n\n        // Set custom name - only done if there is no original one\n        if (!dataContainer.has(StructuredDataKey.CUSTOM_NAME)) {\n            dataContainer.set(StructuredDataKey.CUSTOM_NAME, mappedItem.tagName());\n            customTag.putBoolean(nbtTagName(\"added_custom_name\"), true);\n        }\n    }\n\n    private void addCustomModelData(final Item item, final boolean addOriginalIdentifier, final MappedItem mappedItem, final CompoundTag customTag) {\n        final StructuredDataContainer dataContainer = item.dataContainer();\n        CustomModelData1_21_4 customModelData = dataContainer.get(StructuredDataKey.CUSTOM_MODEL_DATA1_21_4);\n        if (customModelData == null) {\n            final String[] strings = addOriginalIdentifier\n                ? new String[]{protocol.getMappingData().getFullItemMappings().identifier(item.identifier())}\n                : new String[0];\n            customModelData = new CustomModelData1_21_4(\n                mappedItem.customModelData() != null ? new float[]{mappedItem.customModelData().floatValue()} : new float[0],\n                new boolean[0],\n                strings,\n                EMPTY_INT_ARRAY\n            );\n            dataContainer.set(StructuredDataKey.CUSTOM_MODEL_DATA1_21_4, customModelData);\n            // Add one global marker and one for this specific version, so it is removed at the correct protocol\n            customTag.putBoolean(GLOBAL_MODEL_DATA_MARKER, true);\n            customTag.putBoolean(nbtTagName(\"added_custom_model_data\"), true);\n        } else if (addOriginalIdentifier && !customTag.contains(GLOBAL_MODEL_DATA_MARKER)) {\n            final String identifier = protocol.getMappingData().getFullItemMappings().identifier(item.identifier());\n            dataContainer.set(StructuredDataKey.CUSTOM_MODEL_DATA1_21_4, new CustomModelData1_21_4(\n                customModelData.floats(), customModelData.booleans(), ArrayUtil.add(customModelData.strings(), identifier), customModelData.colors()\n            ));\n            customTag.putBoolean(GLOBAL_MODEL_DATA_MARKER, true);\n            customTag.putString(nbtTagName(\"injected_custom_model_data\"), identifier);\n        }\n    }\n\n    @Override\n    protected void restoreBackupData(final Item item, final StructuredDataContainer container, final CompoundTag customData) {\n        super.restoreBackupData(item, container, customData);\n        if (removeBackupTag(customData, \"id\") instanceof final IntTag originalTag) {\n            item.setIdentifier(originalTag.asInt());\n            removeCustomTag(container, customData);\n        }\n\n        if (removeBackupTag(customData, \"injected_custom_model_data\") instanceof StringTag injectedCustomModelData) {\n            customData.remove(GLOBAL_MODEL_DATA_MARKER);\n            container.replace(StructuredDataKey.CUSTOM_MODEL_DATA1_21_4, customModelData -> {\n                final String target = injectedCustomModelData.getValue();\n                final String[] strings = customModelData.strings();\n                for (int i = 0; i < strings.length; i++) {\n                    if (strings[i].equals(target)) {\n                        // Remove the injected string\n                        final String[] filteredStrings = ArrayUtil.remove(strings, i);\n                        return new CustomModelData1_21_4(customModelData.floats(), customModelData.booleans(), filteredStrings, customModelData.colors());\n                    }\n                }\n                return customModelData;\n            });\n        } else if (removeBackupTag(customData, \"added_custom_model_data\") != null) {\n            customData.remove(GLOBAL_MODEL_DATA_MARKER);\n            container.remove(StructuredDataKey.CUSTOM_MODEL_DATA1_21_4);\n        }\n    }\n\n    protected void saveListTag(CompoundTag tag, ListTag<?> original, String name) {\n        // Multiple places might try to backup data\n        String backupName = nbtTagName(name);\n        if (!tag.contains(backupName)) {\n            tag.put(backupName, original.copy());\n        }\n    }\n\n    public <T extends Tag> @Nullable ListTag<T> removeListTag(CompoundTag tag, String tagName, Class<T> tagType) {\n        String backupName = nbtTagName(tagName);\n        ListTag<T> data = tag.getListTag(backupName, tagType);\n        if (data == null) {\n            return null;\n        }\n\n        tag.remove(backupName);\n        return data;\n    }\n\n    protected void saveGenericTagList(CompoundTag tag, List<Tag> original, String name) {\n        // List tags cannot contain tags of different types, so we have to store them a bit more awkwardly as an indexed compound tag\n        String backupName = nbtTagName(name);\n        if (!tag.contains(backupName)) {\n            CompoundTag output = new CompoundTag();\n            for (int i = 0; i < original.size(); i++) {\n                output.put(Integer.toString(i), original.get(i));\n            }\n            tag.put(backupName, output);\n        }\n    }\n\n    protected List<Tag> removeGenericTagList(CompoundTag tag, String name) {\n        String backupName = nbtTagName(name);\n        CompoundTag data = tag.getCompoundTag(backupName);\n        if (data == null) {\n            return null;\n        }\n\n        tag.remove(backupName);\n        return new ArrayList<>(data.values());\n    }\n\n    protected Tag holderSetToTag(final HolderSet set) {\n        if (set.hasIds()) {\n            return new IntArrayTag(set.ids());\n        } else {\n            return new StringTag(set.tagKey());\n        }\n    }\n\n    protected HolderSet restoreHolderSet(final CompoundTag tag, final String key) {\n        final Tag savedTag = tag.get(key);\n        if (savedTag == null) {\n            return HolderSet.of(EMPTY_INT_ARRAY);\n        }\n\n        if (savedTag instanceof StringTag tagKey) {\n            return HolderSet.of(tagKey.getValue());\n        } else if (savedTag instanceof IntArrayTag idsTag) {\n            return HolderSet.of(idsTag.getValue());\n        } else {\n            return HolderSet.of(EMPTY_INT_ARRAY);\n        }\n    }\n\n    protected <V> Tag holderToTag(final Holder<V> holder, final BiConsumer<V, CompoundTag> valueSaveFunction) {\n        if (holder.hasId()) {\n            return new IntTag(holder.id());\n        } else {\n            final CompoundTag savedTag = new CompoundTag();\n            valueSaveFunction.accept(holder.value(), savedTag);\n            return savedTag;\n        }\n    }\n\n    protected <V> Tag eitherHolderToTag(final EitherHolder<V> holder, final BiConsumer<V, CompoundTag> valueSaveFunction) {\n        if (holder.hasKey()) {\n            return new StringTag(holder.key());\n        } else {\n            return holderToTag(holder.holder(), valueSaveFunction);\n        }\n    }\n\n    protected <V> void saveEitherHolderData(final StructuredDataKey<EitherHolder<V>> key, final StructuredDataContainer data, final CompoundTag backupTag, final BiConsumer<V, CompoundTag> valueSaveFunction) {\n        final EitherHolder<V> holder = data.get(key);\n        if (holder != null) {\n            backupTag.put(key.identifier(), eitherHolderToTag(holder, valueSaveFunction));\n        }\n    }\n\n    protected <V> void saveHolderData(final StructuredDataKey<Holder<V>> key, final StructuredDataContainer data, final CompoundTag backupTag, final BiConsumer<V, CompoundTag> valueSaveFunction) {\n        final Holder<V> holder = data.get(key);\n        if (holder != null) {\n            backupTag.put(key.identifier(), holderToTag(holder, valueSaveFunction));\n        }\n    }\n\n    protected <V> Holder<V> restoreHolder(final CompoundTag tag, final String key, final Function<CompoundTag, V> valueRestoreFunction) {\n        final Tag savedTag = tag.get(key);\n        if (savedTag == null) {\n            return Holder.of(0);\n        }\n\n        if (savedTag instanceof IntTag idTag) {\n            return Holder.of(idTag.asInt());\n        } else if (savedTag instanceof CompoundTag compoundTag) {\n            return Holder.of(valueRestoreFunction.apply(compoundTag));\n        } else {\n            return Holder.of(0);\n        }\n    }\n\n    protected <V> EitherHolder<V> restoreEitherHolder(final CompoundTag tag, final String key, final Function<CompoundTag, V> valueRestoreFunction) {\n        final Tag savedTag = tag.get(key);\n        if (savedTag == null) {\n            return EitherHolder.of(Holder.of(0));\n        }\n\n        if (savedTag instanceof StringTag keyTag) {\n            return EitherHolder.of(keyTag.getValue());\n        } else {\n            return EitherHolder.of(restoreHolder(tag, key, valueRestoreFunction));\n        }\n    }\n\n    protected <V> void restoreHolderData(final StructuredDataKey<Holder<V>> key, final StructuredDataContainer data, final CompoundTag backupTag, final Function<CompoundTag, V> valueRestoreFunction) {\n        if (backupTag.contains(key.identifier())) {\n            data.set(key, restoreHolder(backupTag, key.identifier(), valueRestoreFunction));\n        }\n    }\n\n    protected void saveStringData(final StructuredDataKey<String> key, final StructuredDataContainer data, final CompoundTag backupTag) {\n        final String value = data.get(key);\n        if (value != null) {\n            backupTag.putString(key.identifier(), value);\n        }\n    }\n\n    protected void restoreStringData(final StructuredDataKey<String> key, final StructuredDataContainer data, final CompoundTag backupTag) {\n        final String value = backupTag.getString(key.identifier());\n        if (value != null) {\n            data.set(key, value);\n        }\n    }\n\n    protected void saveKeyData(final StructuredDataKey<Key> key, final StructuredDataContainer data, final CompoundTag backupTag) {\n        final Key value = data.get(key);\n        if (value != null) {\n            backupTag.putString(key.identifier(), value.original());\n        }\n    }\n\n    protected void restoreKeyData(final StructuredDataKey<Key> key, final StructuredDataContainer data, final CompoundTag backupTag) {\n        final String value = backupTag.getString(key.identifier());\n        if (value != null) {\n            data.set(key, Key.of(value));\n        }\n    }\n\n    protected void saveIntData(final StructuredDataKey<Integer> key, final StructuredDataContainer data, final CompoundTag backupTag) {\n        final Integer variant = data.get(key);\n        if (variant != null) {\n            backupTag.putInt(key.identifier(), variant);\n        }\n    }\n\n    protected void restoreIntData(final StructuredDataKey<Integer> key, final StructuredDataContainer data, final CompoundTag backupTag) {\n        final IntTag variant = backupTag.getIntTag(key.identifier());\n        if (variant != null) {\n            data.set(key, variant.asInt());\n        }\n    }\n\n    protected void saveFloatData(final StructuredDataKey<Float> key, final StructuredDataContainer data, final CompoundTag backupTag) {\n        final Float variant = data.get(key);\n        if (variant != null) {\n            backupTag.putFloat(key.identifier(), variant);\n        }\n    }\n\n    protected void restoreFloatData(final StructuredDataKey<Float> key, final StructuredDataContainer data, final CompoundTag backupTag) {\n        final FloatTag variant = backupTag.getFloatTag(key.identifier());\n        if (variant != null) {\n            data.set(key, variant.asFloat());\n        }\n    }\n\n    protected void saveSoundEventHolder(final CompoundTag tag, final Holder<SoundEvent> holder) {\n        tag.put(\"sound_event\", holderToTag(holder, this::saveSoundEvent));\n    }\n\n    protected void saveSoundEvent(final SoundEvent soundEvent, final CompoundTag tag) {\n        tag.putString(\"identifier\", soundEvent.identifier());\n        if (soundEvent.fixedRange() != null) {\n            tag.putFloat(\"fixed_range\", soundEvent.fixedRange());\n        }\n    }\n\n    protected Holder<SoundEvent> restoreSoundEventHolder(final CompoundTag tag) {\n        return restoreSoundEventHolder(tag, \"sound_event\");\n    }\n\n    protected Holder<SoundEvent> restoreSoundEventHolder(final CompoundTag tag, final String key) {\n        return restoreHolder(tag, key, soundEventTag -> {\n            final String identifier = soundEventTag.getString(\"identifier\");\n            final FloatTag fixedRange = soundEventTag.getFloatTag(\"fixed_range\");\n            return new SoundEvent(identifier, fixedRange != null ? fixedRange.asFloat() : null);\n        });\n    }\n\n    @Override\n    public String nbtTagName() {\n        return this.nbtTagName;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/api/rewriters/EnchantmentRewriter.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.api.rewriters;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.ListTag;\nimport com.viaversion.nbt.tag.NumberTag;\nimport com.viaversion.nbt.tag.StringTag;\nimport com.viaversion.viabackwards.utils.ChatUtil;\nimport com.viaversion.viaversion.api.minecraft.item.Item;\nimport com.viaversion.viaversion.util.Key;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * Rewriter to handle the addition of new enchantments.\n */\npublic class EnchantmentRewriter {\n\n    public static final String ENCHANTMENT_LEVEL_TRANSLATION = \"enchantment.level.%s\";\n\n    protected final Map<String, String> enchantmentMappings = new HashMap<>();\n    protected final BackwardsItemRewriter<?, ?, ?> itemRewriter;\n    private final boolean jsonFormat;\n\n    public EnchantmentRewriter(BackwardsItemRewriter<?, ?, ?> itemRewriter, boolean jsonFormat) {\n        this.itemRewriter = itemRewriter;\n        this.jsonFormat = jsonFormat;\n    }\n\n    public EnchantmentRewriter(BackwardsItemRewriter<?, ?, ?> itemRewriter) {\n        this(itemRewriter, true);\n    }\n\n    public void registerEnchantment(String key, String replacementLore) {\n        enchantmentMappings.put(Key.stripMinecraftNamespace(key), replacementLore);\n    }\n\n    public void handleToClient(Item item) {\n        CompoundTag tag = item.tag();\n        if (tag == null) return;\n\n        if (tag.getListTag(\"Enchantments\") != null) {\n            rewriteEnchantmentsToClient(tag, false);\n        }\n        if (tag.getListTag(\"StoredEnchantments\") != null) {\n            rewriteEnchantmentsToClient(tag, true);\n        }\n    }\n\n    public void handleToServer(Item item) {\n        CompoundTag tag = item.tag();\n        if (tag == null) return;\n\n        if (tag.contains(itemRewriter.nbtTagName(\"Enchantments\"))) {\n            rewriteEnchantmentsToServer(tag, false);\n        }\n        if (tag.contains(itemRewriter.nbtTagName(\"StoredEnchantments\"))) {\n            rewriteEnchantmentsToServer(tag, true);\n        }\n    }\n\n    public void rewriteEnchantmentsToClient(CompoundTag tag, boolean storedEnchant) {\n        String key = storedEnchant ? \"StoredEnchantments\" : \"Enchantments\";\n        ListTag<CompoundTag> enchantments = tag.getListTag(key, CompoundTag.class);\n        List<StringTag> loreToAdd = new ArrayList<>();\n        boolean changed = false;\n\n        Iterator<CompoundTag> iterator = enchantments.iterator();\n        while (iterator.hasNext()) {\n            CompoundTag enchantmentEntry = iterator.next();\n            StringTag idTag = enchantmentEntry.getStringTag(\"id\");\n            if (idTag == null) {\n                continue;\n            }\n\n            String enchantmentId = Key.stripMinecraftNamespace(idTag.getValue());\n            String remappedName = enchantmentMappings.get(enchantmentId);\n            if (remappedName != null) {\n                if (!changed) {\n                    // Backup original before doing modifications\n                    itemRewriter.saveListTag(tag, enchantments, key);\n                    changed = true;\n                }\n\n                iterator.remove();\n\n                NumberTag levelTag = enchantmentEntry.getNumberTag(\"lvl\");\n                int level = levelTag != null ? levelTag.asInt() : 1;\n                String loreValue;\n                if (jsonFormat) {\n                    loreValue = ChatUtil.legacyToJsonString(remappedName, ENCHANTMENT_LEVEL_TRANSLATION.formatted(level), true);\n                } else {\n                    loreValue = remappedName + \" \" + getRomanNumber(level);\n                }\n\n                loreToAdd.add(new StringTag(loreValue));\n            }\n        }\n\n        if (!loreToAdd.isEmpty()) {\n            // Add dummy enchant for the glow effect if there are no actual enchantments left\n            if (!storedEnchant && enchantments.isEmpty()) {\n                CompoundTag dummyEnchantment = new CompoundTag();\n                dummyEnchantment.putString(\"id\", \"\");\n                dummyEnchantment.putShort(\"lvl\", (short) 0);\n                enchantments.add(dummyEnchantment);\n            }\n\n            CompoundTag display = tag.getCompoundTag(\"display\");\n            if (display == null) {\n                tag.put(\"display\", display = new CompoundTag());\n            }\n\n            ListTag<StringTag> loreTag = display.getListTag(\"Lore\", StringTag.class);\n            if (loreTag == null) {\n                display.put(\"Lore\", loreTag = new ListTag<>(StringTag.class));\n            } else {\n                // Save original lore\n                itemRewriter.saveListTag(display, loreTag, \"Lore\");\n            }\n\n            loreToAdd.addAll(loreTag.getValue());\n            loreTag.setValue(loreToAdd);\n        }\n    }\n\n    public void rewriteEnchantmentsToServer(CompoundTag tag, boolean storedEnchant) {\n        // Just restore the original tag ig present (lore is always restored in the item rewriter)\n        String key = storedEnchant ? \"StoredEnchantments\" : \"Enchantments\";\n        itemRewriter.restoreListTag(tag, key);\n    }\n\n    public static String getRomanNumber(int number) {\n        return switch (number) {\n            case 1 -> \"I\";\n            case 2 -> \"II\";\n            case 3 -> \"III\";\n            case 4 -> \"IV\";\n            case 5 -> \"V\";\n            case 6 -> \"VI\";\n            case 7 -> \"VII\";\n            case 8 -> \"VIII\";\n            case 9 -> \"IX\";\n            case 10 -> \"X\";\n            default -> ENCHANTMENT_LEVEL_TRANSLATION.formatted(number); // Fallback to translation to match vanilla style\n        };\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/api/rewriters/EntityRewriter.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.api.rewriters;\n\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viaversion.api.data.entity.EntityTracker;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityType;\nimport com.viaversion.viaversion.api.minecraft.entitydata.EntityDataType;\nimport com.viaversion.viaversion.api.protocol.packet.ClientboundPacketType;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandler;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.version.Types1_14;\n\npublic abstract class EntityRewriter<C extends ClientboundPacketType, T extends BackwardsProtocol<C, ?, ?, ?>> extends EntityRewriterBase<C, T> {\n\n    protected EntityRewriter(T protocol) {\n        this(protocol, Types1_14.ENTITY_DATA_TYPES.optionalComponentType, Types1_14.ENTITY_DATA_TYPES.booleanType);\n    }\n\n    protected EntityRewriter(T protocol, EntityDataType displayType, EntityDataType displayVisibilityType) {\n        super(protocol, displayType, 2, displayVisibilityType, 3);\n    }\n\n    @Override\n    public void registerTrackerWithData(C packetType) {\n        protocol.registerClientbound(packetType, wrapper -> {\n            wrapper.passthrough(Types.VAR_INT); // Entity ID\n            wrapper.passthrough(Types.UUID); // Entity UUID\n            wrapper.passthrough(Types.VAR_INT); // Entity Type\n            wrapper.passthrough(Types.DOUBLE); // X\n            wrapper.passthrough(Types.DOUBLE); // Y\n            wrapper.passthrough(Types.DOUBLE); // Z\n            wrapper.passthrough(Types.BYTE); // Pitch\n            wrapper.passthrough(Types.BYTE); // Yaw\n            wrapper.passthrough(Types.INT); // Data\n            getSpawnTrackerWithDataHandler().handle(wrapper);\n        });\n    }\n\n    @Override\n    public void registerTrackerWithData1_19(C packetType) {\n        protocol.registerClientbound(packetType, wrapper -> {\n            wrapper.passthrough(Types.VAR_INT); // Entity id\n            wrapper.passthrough(Types.UUID); // Entity UUID\n            wrapper.passthrough(Types.VAR_INT); // Entity type\n            wrapper.passthrough(Types.DOUBLE); // X\n            wrapper.passthrough(Types.DOUBLE); // Y\n            wrapper.passthrough(Types.DOUBLE); // Z\n            wrapper.passthrough(Types.BYTE); // Pitch\n            wrapper.passthrough(Types.BYTE); // Yaw\n            wrapper.passthrough(Types.BYTE); // Head yaw\n            wrapper.passthrough(Types.VAR_INT); // Data\n            getSpawnTrackerWithDataHandler1_19().handle(wrapper);\n        });\n    }\n\n    public PacketHandler getSpawnTrackerWithDataHandler() {\n        return wrapper -> {\n            // Check against the UNMAPPED entity type\n            EntityType entityType = trackAndMapEntity(wrapper);\n            if (entityType == typeFromId(\"falling_block\")) {\n                int blockState = wrapper.get(Types.INT, 0);\n                wrapper.set(Types.INT, 0, protocol.getMappingData().getNewBlockStateId(blockState));\n            }\n        };\n    }\n\n    public PacketHandler getSpawnTrackerWithDataHandler1_19() {\n        return wrapper -> {\n            if (protocol.getMappingData() == null) {\n                return;\n            }\n\n            // Check against the UNMAPPED entity type\n            EntityType entityType = trackAndMapEntity(wrapper);\n            if (entityType == typeFromId(\"falling_block\")) {\n                int blockState = wrapper.get(Types.VAR_INT, 2);\n                wrapper.set(Types.VAR_INT, 2, protocol.getMappingData().getNewBlockStateId(blockState));\n            }\n        };\n    }\n\n    @Override\n    public void registerTracker(C packetType) {\n        protocol.registerClientbound(packetType, wrapper -> {\n            wrapper.passthrough(Types.VAR_INT); // Entity ID\n            wrapper.passthrough(Types.UUID); // Entity UUID\n            wrapper.passthrough(Types.VAR_INT); // Entity Type\n            trackAndMapEntity(wrapper);\n        });\n    }\n\n    /**\n     * Returns a handler to track the current world and uncache entity data on world changes.\n     *\n     * @return handler to track the current world\n     */\n    public PacketHandler worldTrackerHandlerByKey() {\n        return wrapper -> {\n            EntityTracker tracker = tracker(wrapper.user());\n            String world = wrapper.get(Types.STRING, 1);\n            if (tracker.currentWorld() != null && !tracker.currentWorld().equals(world)) {\n                tracker.clearEntities();\n            }\n            tracker.setCurrentWorld(world);\n        };\n    }\n\n    /**\n     * Sets the mapped entity id and returns the unmapped entity type.\n     *\n     * @param wrapper packet wrapper\n     * @return unmapped (!) entity type\n     */\n    protected EntityType trackAndMapEntity(PacketWrapper wrapper) {\n        int typeId = wrapper.get(Types.VAR_INT, 1);\n        EntityType entityType = typeFromId(typeId);\n        if (entityType == null) {\n            return null;\n        }\n\n        tracker(wrapper.user()).addEntity(wrapper.get(Types.VAR_INT, 0), entityType);\n\n        int mappedTypeId = newEntityId(entityType.getId());\n        if (typeId != mappedTypeId) {\n            wrapper.set(Types.VAR_INT, 1, mappedTypeId);\n        }\n\n        return entityType;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/api/rewriters/EntityRewriterBase.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.api.rewriters;\n\nimport com.google.common.base.Preconditions;\nimport com.viaversion.viabackwards.ViaBackwards;\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viabackwards.api.entities.storage.EntityReplacement;\nimport com.viaversion.viabackwards.api.entities.storage.WrappedEntityData;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.data.entity.EntityTracker;\nimport com.viaversion.viaversion.api.data.entity.StoredEntityData;\nimport com.viaversion.viaversion.api.data.entity.TrackedEntity;\nimport com.viaversion.viaversion.api.minecraft.ClientWorld;\nimport com.viaversion.viaversion.api.minecraft.Particle;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityType;\nimport com.viaversion.viaversion.api.minecraft.entitydata.EntityData;\nimport com.viaversion.viaversion.api.minecraft.entitydata.EntityDataType;\nimport com.viaversion.viaversion.api.protocol.packet.ClientboundPacketType;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandler;\nimport com.viaversion.viaversion.api.type.Type;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.libs.fastutil.ints.Int2ObjectMap;\nimport com.viaversion.viaversion.libs.fastutil.ints.Int2ObjectOpenHashMap;\nimport com.viaversion.viaversion.libs.gson.JsonElement;\nimport com.viaversion.viaversion.rewriter.EntityRewriter;\nimport com.viaversion.viaversion.rewriter.entitydata.EntityDataHandlerEvent;\nimport java.util.List;\nimport org.checkerframework.checker.nullness.qual.Nullable;\n\n/**\n * Entity rewriter base class.\n *\n * @see com.viaversion.viabackwards.api.rewriters.EntityRewriter\n * @see LegacyEntityRewriter\n */\npublic abstract class EntityRewriterBase<C extends ClientboundPacketType, T extends BackwardsProtocol<C, ?, ?, ?>> extends EntityRewriter<C, T> {\n    private final Int2ObjectMap<EntityReplacement> entityDataMappings = new Int2ObjectOpenHashMap<>();\n    private final EntityDataType displayNameDataType;\n    private final EntityDataType displayVisibilityDataType;\n    private final int displayNameIndex;\n    private final int displayVisibilityIndex;\n\n    EntityRewriterBase(T protocol, EntityDataType displayNameDataType, int displayNameIndex,\n                       EntityDataType displayVisibilityDataType, int displayVisibilityIndex) {\n        super(protocol, false);\n        this.displayNameDataType = displayNameDataType;\n        this.displayNameIndex = displayNameIndex;\n        this.displayVisibilityDataType = displayVisibilityDataType;\n        this.displayVisibilityIndex = displayVisibilityIndex;\n    }\n\n    @Override\n    public void handleEntityData(int entityId, List<EntityData> entityDataList, UserConnection connection) {\n        final TrackedEntity entity = tracker(connection).entity(entityId);\n        final boolean initialEntityData = !(entity != null && entity.hasSentEntityData());\n\n        super.handleEntityData(entityId, entityDataList, connection);\n\n        if (entity == null) {\n            return; // Don't handle untracked entities - basically always the fault of a plugin sending virtual entities through concurrency-unsafe handling\n        }\n\n        // Set the mapped entity name if there is no custom name set already\n        final EntityReplacement entityMapping = entityDataForType(entity.entityType());\n        final Object displayNameObject;\n        if (entityMapping != null && (displayNameObject = entityMapping.entityName()) != null) {\n            final EntityData displayName = getData(displayNameIndex, entityDataList);\n            if (initialEntityData) {\n                if (displayName == null) {\n                    // Add it as new entity data\n                    entityDataList.add(new EntityData(displayNameIndex, displayNameDataType, displayNameObject));\n                    addDisplayVisibilityData(entityDataList);\n                } else if (displayName.getValue() == null || displayName.getValue().toString().isEmpty()) {\n                    // Overwrite the existing null/empty display name\n                    displayName.setValue(displayNameObject);\n                    addDisplayVisibilityData(entityDataList);\n                }\n            } else if (displayName != null && (displayName.getValue() == null || displayName.getValue().toString().isEmpty())) {\n                // Overwrite null/empty display name\n                displayName.setValue(displayNameObject);\n                addDisplayVisibilityData(entityDataList);\n            }\n        }\n\n        // Add any other extra data for mapped entities\n        if (entityMapping != null && entityMapping.hasBaseData() && initialEntityData) {\n            entityMapping.defaultData().createData(new WrappedEntityData(entityDataList));\n        }\n    }\n\n    private void addDisplayVisibilityData(List<EntityData> entityDataList) {\n        if (alwaysShowOriginalMobName()) {\n            removeData(displayVisibilityIndex, entityDataList);\n            entityDataList.add(new EntityData(displayVisibilityIndex, displayVisibilityDataType, getDisplayVisibilityDataValue()));\n        }\n    }\n\n    protected Object getDisplayVisibilityDataValue() {\n        return true;\n    }\n\n    protected boolean alwaysShowOriginalMobName() {\n        return ViaBackwards.getConfig().alwaysShowOriginalMobName();\n    }\n\n    protected @Nullable EntityData getData(int dataIndex, List<EntityData> entityDataList) {\n        for (EntityData entityData : entityDataList) {\n            if (entityData.id() == dataIndex) {\n                return entityData;\n            }\n        }\n        return null;\n    }\n\n    protected void removeData(int dataIndex, List<EntityData> entityDataList) {\n        entityDataList.removeIf(data -> data.id() == dataIndex);\n    }\n\n    protected boolean hasData(EntityType type) {\n        return entityDataMappings.containsKey(type.getId());\n    }\n\n    protected @Nullable EntityReplacement entityDataForType(EntityType type) {\n        return entityDataMappings.get(type.getId());\n    }\n\n    protected @Nullable StoredEntityData storedEntityData(EntityDataHandlerEvent event) {\n        return tracker(event.user()).entityData(event.entityId());\n    }\n\n    /**\n     * Maps an entity type to another with extra data.\n     * Note that both types should be of the same version.\n     *\n     * @param type       entity type\n     * @param mappedType mapped entity type\n     * @return created entity data\n     * @see #mapEntityType(EntityType, EntityType) for id only rewriting\n     */\n    protected EntityReplacement mapEntityTypeWithData(EntityType type, EntityType mappedType) {\n        Preconditions.checkArgument(type.getClass() == mappedType.getClass(), \"Both entity types need to be of the same class\");\n\n        // Already rewrite the id here\n        int mappedReplacementId = newEntityId(mappedType.getId());\n        EntityReplacement data = new EntityReplacement(protocol, type, mappedReplacementId);\n        mapEntityType(type.getId(), mappedReplacementId);\n        entityDataMappings.put(type.getId(), data);\n        return data;\n    }\n\n    public void registerEntityDataTypeHandler(\n        @Nullable EntityDataType itemType,\n        @Nullable EntityDataType blockStateType,\n        @Nullable EntityDataType optionalBlockStateType,\n        @Nullable EntityDataType particleType,\n        @Nullable EntityDataType componentType,\n        @Nullable EntityDataType optionalComponentType\n    ) {\n        filter().handler((event, data) -> {\n            EntityDataType type = data.dataType();\n            if (type == itemType) {\n                data.setValue(protocol.getItemRewriter().handleItemToClient(event.user(), data.value()));\n            } else if (type == blockStateType) {\n                int value = data.value();\n                data.setValue(protocol.getMappingData().getNewBlockStateId(value));\n            } else if (type == optionalBlockStateType) {\n                int value = data.value();\n                if (value != 0) {\n                    data.setValue(protocol.getMappingData().getNewBlockStateId(value));\n                }\n            } else if (type == particleType) {\n                protocol.getParticleRewriter().rewriteParticle(event.user(), data.value());\n            } else if (type == optionalComponentType || type == componentType) {\n                JsonElement text = data.value();\n                protocol.getComponentRewriter().processText(event.user(), text);\n            }\n        });\n    }\n\n    public void registerEntityDataTypeHandler1_20_3(\n        @Nullable EntityDataType itemType,\n        @Nullable EntityDataType blockStateType,\n        @Nullable EntityDataType optionalBlockStateType,\n        @Nullable EntityDataType particleType,\n        @Nullable EntityDataType particlesType,\n        @Nullable EntityDataType componentType,\n        @Nullable EntityDataType optionalComponentType\n    ) {\n        filter().handler((event, data) -> {\n            EntityDataType type = data.dataType();\n            if (type == itemType) {\n                data.setValue(protocol.getItemRewriter().handleItemToClient(event.user(), data.value()));\n            } else if (type == blockStateType) {\n                int value = data.value();\n                data.setValue(protocol.getMappingData().getNewBlockStateId(value));\n            } else if (type == optionalBlockStateType) {\n                int value = data.value();\n                if (value != 0) {\n                    data.setValue(protocol.getMappingData().getNewBlockStateId(value));\n                }\n            } else if (type == particleType) {\n                protocol.getParticleRewriter().rewriteParticle(event.user(), data.value());\n            } else if (type == particlesType) {\n                Particle[] particles = data.value();\n                for (final Particle particle : particles) {\n                    protocol.getParticleRewriter().rewriteParticle(event.user(), particle);\n                }\n            } else if (type == optionalComponentType || type == componentType) {\n                protocol.getComponentRewriter().processTag(event.user(), data.value());\n            }\n        });\n    }\n\n    // ONLY TRACKS, DOESN'T REWRITE IDS\n    protected PacketHandler getTrackerHandler(Type<? extends Number> intType, int typeIndex) {\n        return wrapper -> {\n            Number id = wrapper.get(intType, typeIndex);\n            EntityType entityType = typeFromId(id.intValue());\n            if (entityType != null) {\n                tracker(wrapper.user()).addEntity(wrapper.get(Types.VAR_INT, 0), entityType);\n            }\n        };\n    }\n\n    protected PacketHandler getTrackerHandler() {\n        return getTrackerHandler(Types.VAR_INT, 1);\n    }\n\n    protected PacketHandler getTrackerHandler(EntityType entityType) {\n        return wrapper -> tracker(wrapper.user()).addEntity(wrapper.get(Types.VAR_INT, 0), entityType);\n    }\n\n    protected PacketHandler getPlayerTrackerHandler() {\n        return wrapper -> {\n            final int entityId = wrapper.get(Types.INT, 0);\n\n            final EntityTracker tracker = tracker(wrapper.user());\n            tracker(wrapper.user()).setClientEntityId(entityId);\n            tracker.addEntity(entityId, tracker.playerType());\n        };\n    }\n\n    protected PacketHandler getDimensionHandler(int index) {\n        return wrapper -> {\n            ClientWorld clientWorld = wrapper.user().getClientWorld(this.protocol.getClass());\n            int dimensionId = wrapper.get(Types.INT, index);\n            if (clientWorld.setEnvironment(dimensionId)) {\n                tracker(wrapper.user()).clearEntities();\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/api/rewriters/LegacyBlockItemRewriter.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.viaversion.viabackwards.api.rewriters;\n\nimport com.viaversion.nbt.tag.ByteTag;\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.IntTag;\nimport com.viaversion.nbt.tag.NumberTag;\nimport com.viaversion.nbt.tag.ShortTag;\nimport com.viaversion.nbt.tag.StringTag;\nimport com.viaversion.nbt.tag.Tag;\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viabackwards.api.data.BackwardsMappingDataLoader;\nimport com.viaversion.viabackwards.api.data.MappedLegacyBlockItem;\nimport com.viaversion.viabackwards.protocol.v1_12to1_11_1.data.BlockColors1_11_1;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.BlockChangeRecord;\nimport com.viaversion.viaversion.api.minecraft.chunks.Chunk;\nimport com.viaversion.viaversion.api.minecraft.chunks.ChunkSection;\nimport com.viaversion.viaversion.api.minecraft.chunks.DataPalette;\nimport com.viaversion.viaversion.api.minecraft.chunks.PaletteType;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_12;\nimport com.viaversion.viaversion.api.minecraft.item.Item;\nimport com.viaversion.viaversion.api.protocol.packet.ClientboundPacketType;\nimport com.viaversion.viaversion.api.protocol.packet.ServerboundPacketType;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandler;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.type.Type;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.libs.fastutil.ints.Int2ObjectMap;\nimport com.viaversion.viaversion.libs.fastutil.ints.Int2ObjectOpenHashMap;\nimport com.viaversion.viaversion.libs.gson.JsonElement;\nimport com.viaversion.viaversion.libs.gson.JsonObject;\nimport com.viaversion.viaversion.libs.gson.JsonPrimitive;\nimport com.viaversion.viaversion.util.ComponentUtil;\nimport com.viaversion.viaversion.util.IdAndData;\nimport java.util.HashMap;\nimport java.util.Map;\nimport org.checkerframework.checker.nullness.qual.Nullable;\n\npublic abstract class LegacyBlockItemRewriter<C extends ClientboundPacketType, S extends ServerboundPacketType,\n    T extends BackwardsProtocol<C, ?, ?, S>> extends BackwardsItemRewriterBase<C, S, T> {\n\n    protected final Int2ObjectMap<MappedLegacyBlockItem> itemReplacements = new Int2ObjectOpenHashMap<>(8); // Raw id -> mapped data\n    protected final Int2ObjectMap<MappedLegacyBlockItem> blockReplacements = new Int2ObjectOpenHashMap<>(8); // Raw id -> mapped data\n\n    protected LegacyBlockItemRewriter(T protocol, String name, Type<Item> itemType, Type<Item[]> itemArrayType, Type<Item> mappedItemType, Type<Item[]> mappedItemArrayType) {\n        super(protocol, itemType, itemArrayType, mappedItemType, mappedItemArrayType, false);\n\n        Int2ObjectMap<MappedLegacyBlockItem> blockItemReplacements = new Int2ObjectOpenHashMap<>(8);\n        final JsonObject jsonObject = readMappingsFile(\"item-mappings-\" + name + \".json\");\n        addMappings(MappedLegacyBlockItem.Type.ITEM, jsonObject, itemReplacements);\n        addMappings(MappedLegacyBlockItem.Type.BLOCK_ITEM, jsonObject, blockItemReplacements);\n        addMappings(MappedLegacyBlockItem.Type.BLOCK, jsonObject, blockReplacements);\n\n        blockReplacements.putAll(blockItemReplacements);\n        itemReplacements.putAll(blockItemReplacements);\n    }\n\n    protected LegacyBlockItemRewriter(T protocol, String name, Type<Item> itemType, Type<Item[]> itemArrayType) {\n        this(protocol, name, itemType, itemArrayType, itemType, itemArrayType);\n    }\n\n    protected LegacyBlockItemRewriter(T protocol, String name) {\n        this(protocol, name, Types.ITEM1_8, Types.ITEM1_8_SHORT_ARRAY);\n    }\n\n    private void addMappings(MappedLegacyBlockItem.Type type, JsonObject object, Int2ObjectMap<MappedLegacyBlockItem> mappings) {\n        if (object.has(type.getName())) {\n            final JsonObject mappingsObject = object.getAsJsonObject(type.getName());\n            for (Map.Entry<String, JsonElement> dataEntry : mappingsObject.entrySet()) {\n                addMapping(dataEntry.getKey(), dataEntry.getValue().getAsJsonObject(), type, mappings);\n            }\n        }\n    }\n\n    private void addMapping(String key, JsonObject object, MappedLegacyBlockItem.Type type, Int2ObjectMap<MappedLegacyBlockItem> mappings) {\n        int id = object.getAsJsonPrimitive(\"id\").getAsInt();\n        JsonPrimitive jsonData = object.getAsJsonPrimitive(\"data\");\n        short data = jsonData != null ? jsonData.getAsShort() : 0;\n        String name = type != MappedLegacyBlockItem.Type.BLOCK ? object.getAsJsonPrimitive(\"name\").getAsString() : null;\n\n        if (key.indexOf('-') == -1) {\n            int unmappedId;\n            int dataSeparatorIndex = key.indexOf(':');\n            if (dataSeparatorIndex != -1) {\n                // Include data\n                short unmappedData = Short.parseShort(key.substring(dataSeparatorIndex + 1));\n                unmappedId = Integer.parseInt(key.substring(0, dataSeparatorIndex));\n                unmappedId = compress(unmappedId, unmappedData);\n            } else {\n                unmappedId = compress(Integer.parseInt(key), -1);\n            }\n\n            mappings.put(unmappedId, new MappedLegacyBlockItem(id, data, name, type));\n            return;\n        }\n\n        // Range of ids\n        String[] split = key.split(\"-\", 2);\n        int from = Integer.parseInt(split[0]);\n        int to = Integer.parseInt(split[1]);\n\n        // Special block color handling\n        if (name != null && name.contains(\"%color%\")) {\n            for (int i = from; i <= to; i++) {\n                mappings.put(compress(i, -1), new MappedLegacyBlockItem(id, data, name.replace(\"%color%\", BlockColors1_11_1.get(i - from)), type));\n            }\n        } else {\n            MappedLegacyBlockItem mappedBlockItem = new MappedLegacyBlockItem(id, data, name, type);\n            for (int i = from; i <= to; i++) {\n                mappings.put(compress(i, -1), mappedBlockItem);\n            }\n        }\n    }\n\n    public void registerBlockChange(C packetType) {\n        protocol.registerClientbound(packetType, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.BLOCK_POSITION1_8); // 0 - Block Position\n                map(Types.VAR_INT); // 1 - Block\n\n                handler(wrapper -> {\n                    int idx = wrapper.get(Types.VAR_INT, 0);\n                    wrapper.set(Types.VAR_INT, 0, handleBlockId(idx));\n                });\n            }\n        });\n    }\n\n    public void registerMultiBlockChange(C packetType) {\n        protocol.registerClientbound(packetType, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.INT); // 0 - Chunk X\n                map(Types.INT); // 1 - Chunk Z\n                map(Types.BLOCK_CHANGE_ARRAY);\n\n                handler(wrapper -> {\n                    for (BlockChangeRecord record : wrapper.get(Types.BLOCK_CHANGE_ARRAY, 0)) {\n                        record.setBlockId(handleBlockId(record.getBlockId()));\n                    }\n                });\n            }\n        });\n    }\n\n    @Override\n    public @Nullable Item handleItemToClient(UserConnection connection, @Nullable Item item) {\n        if (item == null) return null;\n\n        MappedLegacyBlockItem data = getMappedItem(item.identifier(), item.data());\n        if (data == null) {\n            // Just rewrite the id\n            return super.handleItemToClient(connection, item);\n        }\n        if (item.tag() == null) {\n            item.setTag(new CompoundTag());\n        }\n\n        short originalData = item.data();\n        item.tag().putInt(nbtTagName(\"id\"), item.identifier());\n        item.setIdentifier(data.getId());\n        // Keep original data if mapped data is set to -1\n        if (data.getData() != -1) {\n            item.setData(data.getData());\n            item.tag().putShort(nbtTagName(\"data\"), originalData);\n        }\n\n        // Set display name\n        if (data.getName() != null) {\n            CompoundTag display = item.tag().getCompoundTag(\"display\");\n            if (display == null) {\n                item.tag().put(\"display\", display = new CompoundTag());\n            }\n\n            StringTag nameTag = display.getStringTag(\"Name\");\n            if (nameTag == null) {\n                nameTag = new StringTag(data.getName());\n                display.put(\"Name\", nameTag);\n                display.put(nbtTagName(\"customName\"), new ByteTag(false));\n            }\n\n            // Handle colors\n            String value = nameTag.getValue();\n            if (value.contains(\"%vb_color%\")) {\n                display.putString(\"Name\", value.replace(\"%vb_color%\", BlockColors1_11_1.get(originalData)));\n            }\n        }\n        return item;\n    }\n\n    @Override\n    public @Nullable Item handleItemToServer(UserConnection connection, @Nullable Item item) {\n        if (item == null) return null;\n        item = super.handleItemToServer(connection, item);\n        if (item.tag() != null) {\n            Tag originalId = item.tag().remove(nbtTagName(\"id\"));\n            if (originalId instanceof IntTag) {\n                item.setIdentifier(((NumberTag) originalId).asInt());\n            }\n            Tag originalData = item.tag().remove(nbtTagName(\"data\"));\n            if (originalData instanceof ShortTag) {\n                item.setData(((NumberTag) originalData).asShort());\n            }\n        }\n        return item;\n    }\n\n    public PacketHandler getFallingBlockHandler() {\n        return wrapper -> {\n            final int objectData = wrapper.get(Types.INT, 0);\n            final EntityTypes1_12.ObjectType type = EntityTypes1_12.ObjectType.findById(wrapper.get(Types.BYTE, 0), objectData);\n            if (type == EntityTypes1_12.ObjectType.FALLING_BLOCK) {\n                final IdAndData block = handleBlock(objectData & 4095, objectData >> 12 & 15);\n                if (block == null) return;\n\n                wrapper.set(Types.INT, 0, block.getId() | block.getData() << 12);\n            }\n        };\n    }\n\n    public @Nullable IdAndData handleBlock(int blockId, int data) {\n        MappedLegacyBlockItem settings = getMappedBlock(blockId, data);\n        if (settings == null) {\n            return null;\n        }\n\n        IdAndData block = settings.getBlock();\n        // For some blocks, the data can still be useful (:\n        if (block.getData() == -1) {\n            return block.withData(data);\n        }\n        return block;\n    }\n\n    public int handleBlockId(final int rawId) {\n        final int id = IdAndData.getId(rawId);\n        final int data = IdAndData.getData(rawId);\n\n        final IdAndData mappedBlock = handleBlock(id, data);\n        if (mappedBlock == null) return rawId;\n\n        return IdAndData.toRawData(mappedBlock.getId(), mappedBlock.getData());\n    }\n\n    public void handleChunk(Chunk chunk) {\n        // Map Block Entities\n        Map<Pos, CompoundTag> tags = new HashMap<>();\n        for (CompoundTag tag : chunk.getBlockEntities()) {\n            NumberTag xTag;\n            NumberTag yTag;\n            NumberTag zTag;\n            if ((xTag = tag.getNumberTag(\"x\")) == null\n                || (yTag = tag.getNumberTag(\"y\")) == null\n                || (zTag = tag.getNumberTag(\"z\")) == null) {\n                continue;\n            }\n\n            Pos pos = new Pos(xTag.asInt() & 0xF, yTag.asInt(), zTag.asInt() & 0xF);\n            tags.put(pos, tag);\n\n            // Handle given Block Entities\n            if (pos.y() < 0 || pos.y() > 255) continue; // 1.17\n\n            ChunkSection section = chunk.getSections()[pos.y() >> 4];\n            if (section == null) continue;\n\n            int block = section.palette(PaletteType.BLOCKS).idAt(pos.x(), pos.y() & 0xF, pos.z());\n\n            MappedLegacyBlockItem settings = getMappedBlock(block);\n            if (settings != null && settings.hasBlockEntityHandler()) {\n                settings.getBlockEntityHandler().handleCompoundTag(block, tag);\n            }\n        }\n\n        for (int i = 0; i < chunk.getSections().length; i++) {\n            ChunkSection section = chunk.getSections()[i];\n            if (section == null) {\n                continue;\n            }\n\n            boolean hasBlockEntityHandler = false;\n\n            // Map blocks\n            DataPalette palette = section.palette(PaletteType.BLOCKS);\n            for (int j = 0; j < palette.size(); j++) {\n                int block = palette.idByIndex(j);\n                int btype = block >> 4;\n                int meta = block & 0xF;\n\n                IdAndData b = handleBlock(btype, meta);\n                if (b != null) {\n                    palette.setIdByIndex(j, IdAndData.toRawData(b.getId(), b.getData()));\n                }\n\n                // We already know that is has a handler\n                if (hasBlockEntityHandler) continue;\n\n                MappedLegacyBlockItem settings = getMappedBlock(block);\n                if (settings != null && settings.hasBlockEntityHandler()) {\n                    hasBlockEntityHandler = true;\n                }\n            }\n\n            if (!hasBlockEntityHandler) continue;\n\n            // We need to handle a Block Entity :(\n            for (int x = 0; x < 16; x++) {\n                for (int y = 0; y < 16; y++) {\n                    for (int z = 0; z < 16; z++) {\n                        int block = palette.idAt(x, y, z);\n\n                        MappedLegacyBlockItem settings = getMappedBlock(block);\n                        if (settings == null || !settings.hasBlockEntityHandler()) continue;\n\n                        Pos pos = new Pos(x, (y + (i << 4)), z);\n\n                        // Already handled above\n                        if (tags.containsKey(pos)) continue;\n\n                        CompoundTag tag = new CompoundTag();\n                        tag.putInt(\"x\", x + (chunk.getX() << 4));\n                        tag.putInt(\"y\", y + (i << 4));\n                        tag.putInt(\"z\", z + (chunk.getZ() << 4));\n\n                        settings.getBlockEntityHandler().handleCompoundTag(block, tag);\n                        chunk.getBlockEntities().add(tag);\n                    }\n                }\n            }\n        }\n    }\n\n    protected CompoundTag getNamedTag(String text) {\n        CompoundTag tag = new CompoundTag();\n        CompoundTag displayTag = new CompoundTag();\n        tag.put(\"display\", displayTag);\n        text = \"§r\" + text;\n        displayTag.putString(\"Name\", jsonNameFormat ? ComponentUtil.legacyToJsonString(text) : text);\n        return tag;\n    }\n\n    private @Nullable MappedLegacyBlockItem getMappedBlock(int id, int data) {\n        MappedLegacyBlockItem mapping = blockReplacements.get(compress(id, data));\n        return mapping != null ? mapping : blockReplacements.get(compress(id, -1));\n    }\n\n    private @Nullable MappedLegacyBlockItem getMappedItem(int id, int data) {\n        MappedLegacyBlockItem mapping = itemReplacements.get(compress(id, data));\n        return mapping != null ? mapping : itemReplacements.get(compress(id, -1));\n    }\n\n    private @Nullable MappedLegacyBlockItem getMappedBlock(int rawId) {\n        int id = IdAndData.getId(rawId);\n        int data = IdAndData.getData(rawId);\n        return getMappedBlock(id, data);\n    }\n\n    protected JsonObject readMappingsFile(final String name) {\n        return BackwardsMappingDataLoader.INSTANCE.loadFromDataDir(name);\n    }\n\n    protected int compress(final int id, final int data) {\n        // Using IdAndData for the internal storage can cause id overlaps in edge cases and would lead to wrong data\n        return (id << 16) | (data & 0xFFFF);\n    }\n\n    private record Pos(int x, short y, int z) {\n\n        public Pos(int x, int y, int z) {\n            this(x, (short) y, z);\n        }\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/api/rewriters/LegacyEnchantmentRewriter.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.api.rewriters;\n\nimport com.viaversion.nbt.tag.ByteTag;\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.IntTag;\nimport com.viaversion.nbt.tag.ListTag;\nimport com.viaversion.nbt.tag.NumberTag;\nimport com.viaversion.nbt.tag.StringTag;\nimport com.viaversion.nbt.tag.Tag;\nimport com.viaversion.viaversion.api.minecraft.item.Item;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\npublic class LegacyEnchantmentRewriter {\n\n    private final Map<Short, String> enchantmentMappings = new HashMap<>();\n    private final String nbtTagName;\n    private final boolean dummyEnchantment;\n\n    private Set<Short> hideLevelForEnchants;\n\n    public LegacyEnchantmentRewriter(String nbtTagName) {\n        this(nbtTagName, true);\n    }\n\n    public LegacyEnchantmentRewriter(String nbtTagName, boolean dummyEnchantment) {\n        this.nbtTagName = nbtTagName;\n        this.dummyEnchantment = dummyEnchantment;\n    }\n\n    public void registerEnchantment(int id, String replacementLore) {\n        enchantmentMappings.put((short) id, replacementLore);\n    }\n\n    public void handleToClient(Item item) {\n        CompoundTag tag = item.tag();\n        if (tag == null) return;\n\n        if (tag.getListTag(\"ench\") != null) {\n            rewriteEnchantmentsToClient(tag, false);\n        }\n        if (tag.getListTag(\"StoredEnchantments\") != null) {\n            rewriteEnchantmentsToClient(tag, true);\n        }\n    }\n\n    public void handleToServer(Item item) {\n        CompoundTag tag = item.tag();\n        if (tag == null) return;\n\n        if (tag.getListTag(nbtTagName + \"|ench\", CompoundTag.class) != null) {\n            rewriteEnchantmentsToServer(tag, false);\n        }\n        if (tag.getListTag(nbtTagName + \"|StoredEnchantments\", CompoundTag.class) != null) {\n            rewriteEnchantmentsToServer(tag, true);\n        }\n    }\n\n    public void rewriteEnchantmentsToClient(CompoundTag tag, boolean storedEnchant) {\n        String key = storedEnchant ? \"StoredEnchantments\" : \"ench\";\n        ListTag<CompoundTag> enchantments = tag.getListTag(key, CompoundTag.class);\n        ListTag<CompoundTag> remappedEnchantments = new ListTag<>(CompoundTag.class);\n        List<StringTag> lore = new ArrayList<>();\n        for (CompoundTag enchantmentEntry : enchantments.copy()) {\n            NumberTag idTag = enchantmentEntry.getNumberTag(\"id\");\n            if (idTag == null) continue;\n\n            short newId = idTag.asShort();\n            String enchantmentName = enchantmentMappings.get(newId);\n            if (enchantmentName != null) {\n                enchantments.remove(enchantmentEntry);\n                NumberTag levelTag = enchantmentEntry.getNumberTag(\"lvl\");\n                short level = levelTag != null ? levelTag.asShort() : 1;\n                if (hideLevelForEnchants != null && hideLevelForEnchants.contains(newId)) {\n                    lore.add(new StringTag(enchantmentName));\n                } else {\n                    lore.add(new StringTag(enchantmentName + \" \" + EnchantmentRewriter.getRomanNumber(level)));\n                }\n                remappedEnchantments.add(enchantmentEntry);\n            }\n        }\n        if (!lore.isEmpty()) {\n            if (this.dummyEnchantment && !storedEnchant && enchantments.isEmpty()) {\n                CompoundTag dummyEnchantment = new CompoundTag();\n                dummyEnchantment.putShort(\"id\", (short) 0);\n                dummyEnchantment.putShort(\"lvl\", (short) 0);\n\n                enchantments.add(dummyEnchantment);\n                tag.put(nbtTagName + \"|dummyEnchant\", new ByteTag(false));\n\n                NumberTag hideFlags = tag.getNumberTag(\"HideFlags\");\n                if (hideFlags == null) {\n                    hideFlags = new IntTag();\n                } else {\n                    tag.putInt(nbtTagName + \"|oldHideFlags\", hideFlags.asByte());\n                }\n\n                int flags = hideFlags.asByte() | 1;\n                tag.putInt(\"HideFlags\", flags);\n            }\n\n            tag.put(nbtTagName + \"|\" + key, remappedEnchantments);\n\n            CompoundTag display = tag.getCompoundTag(\"display\");\n            if (display == null) {\n                tag.put(\"display\", display = new CompoundTag());\n            }\n            ListTag<StringTag> loreTag = display.getListTag(\"Lore\", StringTag.class);\n            if (loreTag == null) {\n                display.put(\"Lore\", loreTag = new ListTag<>(StringTag.class));\n            }\n\n            lore.addAll(loreTag.getValue());\n            loreTag.setValue(lore);\n        }\n    }\n\n    public void rewriteEnchantmentsToServer(CompoundTag tag, boolean storedEnchant) {\n        String key = storedEnchant ? \"StoredEnchantments\" : \"ench\";\n        ListTag<CompoundTag> enchantments = tag.getListTag(key, CompoundTag.class);\n        if (enchantments == null) {\n            enchantments = new ListTag<>(CompoundTag.class);\n        }\n\n        if (!storedEnchant && tag.remove(nbtTagName + \"|dummyEnchant\") != null) {\n            for (CompoundTag enchantment : enchantments.copy()) {\n                NumberTag idTag = enchantment.getNumberTag(\"id\");\n                NumberTag levelTag = enchantment.getNumberTag(\"lvl\");\n                short id = idTag != null ? idTag.asShort() : 0;\n                short level = levelTag != null ? levelTag.asShort() : 0;\n                if (id == 0 && level == 0) {\n                    enchantments.remove(enchantment);\n                }\n            }\n\n            Tag hideFlags = tag.remove(nbtTagName + \"|oldHideFlags\");\n            if (hideFlags instanceof IntTag intTag) {\n                tag.putInt(\"HideFlags\", intTag.asByte());\n            } else {\n                tag.remove(\"HideFlags\");\n            }\n        }\n\n        CompoundTag display = tag.getCompoundTag(\"display\");\n        // A few null checks just to be safe, though they shouldn't actually be\n        ListTag<StringTag> lore = display != null ? display.getListTag(\"Lore\", StringTag.class) : null;\n        ListTag<CompoundTag> remappedEnchantments = (ListTag<CompoundTag>) tag.remove(nbtTagName + \"|\" + key);\n        for (CompoundTag enchantment : remappedEnchantments.copy()) {\n            enchantments.add(enchantment);\n            if (lore != null && !lore.isEmpty()) {\n                lore.remove(lore.get(0));\n            }\n        }\n        if (lore != null && lore.isEmpty()) {\n            display.remove(\"Lore\");\n            if (display.isEmpty()) {\n                tag.remove(\"display\");\n            }\n        }\n\n        tag.put(key, enchantments);\n    }\n\n    public void setHideLevelForEnchants(int... enchants) {\n        this.hideLevelForEnchants = new HashSet<>();\n        for (int enchant : enchants) {\n            hideLevelForEnchants.add((short) enchant);\n        }\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/api/rewriters/LegacyEntityRewriter.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.api.rewriters;\n\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viabackwards.api.entities.storage.EntityObjectData;\nimport com.viaversion.viabackwards.api.entities.storage.EntityReplacement;\nimport com.viaversion.viabackwards.api.entities.storage.WrappedEntityData;\nimport com.viaversion.viaversion.api.minecraft.ClientWorld;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityType;\nimport com.viaversion.viaversion.api.minecraft.entities.ObjectType;\nimport com.viaversion.viaversion.api.minecraft.entitydata.EntityData;\nimport com.viaversion.viaversion.api.minecraft.entitydata.EntityDataType;\nimport com.viaversion.viaversion.api.minecraft.entitydata.types.EntityDataTypes1_9;\nimport com.viaversion.viaversion.api.protocol.packet.ClientboundPacketType;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandler;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.type.Type;\nimport com.viaversion.viaversion.api.type.Types;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport org.checkerframework.checker.nullness.qual.Nullable;\n\npublic abstract class LegacyEntityRewriter<C extends ClientboundPacketType, T extends BackwardsProtocol<C, ?, ?, ?>> extends EntityRewriterBase<C, T> {\n    private final Map<ObjectType, EntityReplacement> objectTypes = new HashMap<>();\n\n    protected LegacyEntityRewriter(T protocol) {\n        this(protocol, EntityDataTypes1_9.STRING, EntityDataTypes1_9.BOOLEAN);\n    }\n\n    protected LegacyEntityRewriter(T protocol, EntityDataType displayType, EntityDataType displayVisibilityType) {\n        super(protocol, displayType, 2, displayVisibilityType, 3);\n    }\n\n    protected EntityObjectData mapObjectType(ObjectType oldObjectType, ObjectType replacement, int data) {\n        EntityObjectData entData = new EntityObjectData(protocol, oldObjectType.getType().name(), oldObjectType.getId(), replacement.getId(), data);\n        objectTypes.put(oldObjectType, entData);\n        return entData;\n    }\n\n    protected @Nullable EntityReplacement getObjectData(ObjectType type) {\n        return objectTypes.get(type);\n    }\n\n    protected void registerRespawn(C packetType) {\n        protocol.registerClientbound(packetType, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.INT);\n                handler(wrapper -> {\n                    ClientWorld clientWorld = wrapper.user().getClientWorld(protocol.getClass());\n                    if (clientWorld.setEnvironment(wrapper.get(Types.INT, 0))) {\n                        tracker(wrapper.user()).clearEntities();\n                    }\n                });\n            }\n        });\n    }\n\n    protected void registerJoinGame(C packetType, EntityType playerType) {\n        protocol.registerClientbound(packetType, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.INT); // 0 - Entity ID\n                map(Types.UNSIGNED_BYTE); // 1 - Gamemode\n                map(Types.INT); // 2 - Dimension\n                handler(wrapper -> {\n                    ClientWorld clientWorld = wrapper.user().getClientWorld(protocol.getClass());\n                    clientWorld.setEnvironment(wrapper.get(Types.INT, 1));\n\n                    final int entityId = wrapper.get(Types.INT, 0);\n                    tracker(wrapper.user()).addEntity(entityId, playerType);\n                    tracker(wrapper.user()).setClientEntityId(entityId);\n                });\n            }\n        });\n    }\n\n    protected PacketHandler getMobSpawnRewriter(Type<List<EntityData>> dataType, IdSetter idSetter) {\n        return wrapper -> {\n            int entityId = wrapper.get(Types.VAR_INT, 0);\n            EntityType type = tracker(wrapper.user()).entityType(entityId);\n            if (type == null) {\n                return;\n            }\n\n            List<EntityData> entityDataList = wrapper.get(dataType, 0);\n            handleEntityData(entityId, entityDataList, wrapper.user());\n\n            EntityReplacement entityReplacement = entityDataForType(type);\n            if (entityReplacement != null) {\n                idSetter.setId(wrapper, entityReplacement.replacementId());\n                if (entityReplacement.hasBaseData()) {\n                    entityReplacement.defaultData().createData(new WrappedEntityData(entityDataList));\n                }\n            }\n        };\n    }\n\n    public PacketHandler getMobSpawnRewriter(Type<List<EntityData>> dataType) {\n        return getMobSpawnRewriter(dataType, (wrapper, id) -> wrapper.set(Types.UNSIGNED_BYTE, 0, (short) id));\n    }\n\n    public PacketHandler getMobSpawnRewriter1_11(Type<List<EntityData>> dataType) {\n        return getMobSpawnRewriter(dataType, (wrapper, id) -> wrapper.set(Types.VAR_INT, 1, id));\n    }\n\n    protected PacketHandler getObjectTrackerHandler() {\n        return wrapper -> {\n            int id = wrapper.get(Types.BYTE, 0);\n            int data = wrapper.get(Types.INT, 0);\n            EntityType type = objectTypeFromId(id, data);\n            if (type == null) {\n                return;\n            }\n\n            tracker(wrapper.user()).addEntity(wrapper.get(Types.VAR_INT, 0), type);\n        };\n    }\n\n    protected PacketHandler getTrackerAndDataHandler(Type<List<EntityData>> dataType, EntityType entityType) {\n        return wrapper -> {\n            tracker(wrapper.user()).addEntity(wrapper.get(Types.VAR_INT, 0), entityType);\n            List<EntityData> entityDataList = wrapper.get(dataType, 0);\n            handleEntityData(wrapper.get(Types.VAR_INT, 0), entityDataList, wrapper.user());\n        };\n    }\n\n    protected PacketHandler getObjectRewriter(ObjectTypeGetter objectGetter) {\n        return wrapper -> {\n            int id = wrapper.get(Types.BYTE, 0);\n            int data = wrapper.get(Types.INT, 0);\n            ObjectType type = objectGetter.get(id, data);\n            if (type == null) {\n                return;\n            }\n\n            EntityReplacement replacement = getObjectData(type);\n            if (replacement != null) {\n                wrapper.set(Types.BYTE, 0, (byte) replacement.replacementId());\n                if (replacement.objectData() != -1) {\n                    wrapper.set(Types.INT, 0, replacement.objectData());\n                }\n            }\n        };\n    }\n\n    @FunctionalInterface\n    protected interface IdSetter {\n\n        void setId(PacketWrapper wrapper, int id);\n    }\n\n    @FunctionalInterface\n    protected interface ObjectTypeGetter {\n\n        ObjectType get(int id, int data);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/api/rewriters/LegacySoundRewriter.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.viaversion.viabackwards.api.rewriters;\n\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viaversion.api.rewriter.RewriterBase;\nimport com.viaversion.viaversion.libs.fastutil.ints.Int2ObjectMap;\nimport com.viaversion.viaversion.libs.fastutil.ints.Int2ObjectOpenHashMap;\n\n@Deprecated\npublic abstract class LegacySoundRewriter<T extends BackwardsProtocol<?, ?, ?, ?>> extends RewriterBase<T> {\n    protected final Int2ObjectMap<SoundData> soundRewrites = new Int2ObjectOpenHashMap<>(64);\n\n    protected LegacySoundRewriter(T protocol) {\n        super(protocol);\n    }\n\n    public SoundData added(int id, int replacement) {\n        return added(id, replacement, -1);\n    }\n\n    public SoundData added(int id, int replacement, float newPitch) {\n        SoundData data = new SoundData(replacement, true, newPitch, true);\n        soundRewrites.put(id, data);\n        return data;\n    }\n\n    public SoundData removed(int id) {\n        SoundData data = new SoundData(-1, false, -1, false);\n        soundRewrites.put(id, data);\n        return data;\n    }\n\n    public int handleSounds(int soundId) {\n        int newSoundId = soundId;\n        SoundData data = soundRewrites.get(soundId);\n        if (data != null) return data.replacementSound();\n\n        for (Int2ObjectMap.Entry<SoundData> entry : soundRewrites.int2ObjectEntrySet()) {\n            if (soundId > entry.getIntKey()) {\n                if (entry.getValue().added()) {\n                    newSoundId--;\n                } else {\n                    newSoundId++;\n                }\n            }\n        }\n        return newSoundId;\n    }\n\n    public boolean hasPitch(int soundId) {\n        SoundData data = soundRewrites.get(soundId);\n        return data != null && data.changePitch();\n    }\n\n    public float handlePitch(int soundId) {\n        SoundData data = soundRewrites.get(soundId);\n        return data != null ? data.newPitch() : 1F;\n    }\n\n    public record SoundData(int replacementSound, boolean changePitch, float newPitch, boolean added) {\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/api/rewriters/MapColorRewriter.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.api.rewriters;\n\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandler;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.rewriter.IdRewriteFunction;\n\npublic final class MapColorRewriter {\n\n    /**\n     * Rewrite map colors using the provided id rewriter.\n     *\n     * @param wrapper   packet wrapper\n     * @param rewriter  id rewriter returning mapped colors, or -1 if unmapped\n     * @param iconCount number of icons to read\n     */\n    public static void rewriteMapColors(PacketWrapper wrapper, IdRewriteFunction rewriter, int iconCount) {\n        for (int i = 0; i < iconCount; i++) {\n            wrapper.passthrough(Types.VAR_INT); // Type\n            wrapper.passthrough(Types.BYTE); // X\n            wrapper.passthrough(Types.BYTE); // Z\n            wrapper.passthrough(Types.BYTE); // Direction\n            wrapper.passthrough(Types.OPTIONAL_COMPONENT); // Display Name\n        }\n\n        short columns = wrapper.passthrough(Types.UNSIGNED_BYTE);\n        if (columns < 1) {\n            return;\n        }\n\n        wrapper.passthrough(Types.UNSIGNED_BYTE); // Rows\n        wrapper.passthrough(Types.UNSIGNED_BYTE); // X\n        wrapper.passthrough(Types.UNSIGNED_BYTE); // Z\n        byte[] data = wrapper.passthrough(Types.BYTE_ARRAY_PRIMITIVE);\n        for (int i = 0; i < data.length; i++) {\n            int color = data[i] & 0xFF;\n            int mappedColor = rewriter.rewrite(color);\n            if (mappedColor != -1) {\n                data[i] = (byte) mappedColor;\n            }\n        }\n    }\n\n    /**\n     * Returns a packethandler to rewrite map data color ids. Reading starts from the icon count.\n     *\n     * @param rewriter id rewriter returning mapped colors, or -1 if unmapped\n     * @return packethandler to rewrite map data color ids\n     */\n    public static PacketHandler getRewriteHandler(IdRewriteFunction rewriter) {\n        return wrapper -> rewriteMapColors(wrapper, rewriter, wrapper.passthrough(Types.VAR_INT));\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/api/rewriters/SoundRewriter.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.api.rewriters;\n\nimport com.viaversion.viaversion.api.data.Mappings;\nimport com.viaversion.viaversion.api.protocol.AbstractProtocol;\nimport com.viaversion.viaversion.api.protocol.packet.ClientboundPacketType;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandler;\nimport com.viaversion.viaversion.api.type.Types;\n\npublic class SoundRewriter<C extends ClientboundPacketType> extends com.viaversion.viaversion.rewriter.SoundRewriter<C> {\n\n    public SoundRewriter(final AbstractProtocol<C, ?, ?, ?> protocol) {\n        super(protocol);\n    }\n\n    public void registerNamedSound(final C packetType) {\n        if (protocol.getMappingData() == null || Mappings.isFullIdentity(protocol.getMappingData().getFullSoundMappings())) {\n            return;\n        }\n        protocol.registerClientbound(packetType, wrapper -> {\n            wrapper.passthrough(Types.STRING); // Sound identifier\n            getNamedSoundHandler().handle(wrapper);\n        });\n    }\n\n    public void registerStopSound(final C packetType) {\n        if (protocol.getMappingData() == null || Mappings.isFullIdentity(protocol.getMappingData().getFullSoundMappings())) {\n            return;\n        }\n        protocol.registerClientbound(packetType, wrapper -> {\n            final byte flags = wrapper.passthrough(Types.BYTE);\n            if ((flags & 0x01) != 0) {\n                wrapper.passthrough(Types.VAR_INT); // Source\n            }\n\n            if ((flags & 0x02) == 0) return; // No sound specified\n            final String soundId = wrapper.read(Types.STRING);\n            final String mappedId = protocol.getMappingData().getFullSoundMappings().mappedIdentifier(soundId);\n            if (mappedId == null) {\n                // No mapping found\n                wrapper.write(Types.STRING, soundId);\n                return;\n            }\n\n            if (!mappedId.isEmpty()) {\n                wrapper.write(Types.STRING, mappedId);\n            } else {\n                // Cancel if set to empty\n                wrapper.cancel();\n            }\n        });\n    }\n\n    public PacketHandler getNamedSoundHandler() {\n        return wrapper -> {\n            final String soundId = wrapper.get(Types.STRING, 0);\n            final String mappedId = protocol.getMappingData().getFullSoundMappings().mappedIdentifier(soundId);\n            if (mappedId == null) {\n                return;\n            }\n\n            if (!mappedId.isEmpty()) {\n                wrapper.set(Types.STRING, 0, mappedId);\n            } else {\n                wrapper.cancel();\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/api/rewriters/StructuredEnchantmentRewriter.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.api.rewriters;\n\nimport com.viaversion.nbt.tag.ByteTag;\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.ListTag;\nimport com.viaversion.nbt.tag.NumberTag;\nimport com.viaversion.nbt.tag.Tag;\nimport com.viaversion.viabackwards.api.data.BackwardsMappingData;\nimport com.viaversion.viabackwards.utils.ChatUtil;\nimport com.viaversion.viaversion.api.data.Mappings;\nimport com.viaversion.viaversion.api.minecraft.data.StructuredDataContainer;\nimport com.viaversion.viaversion.api.minecraft.data.StructuredDataKey;\nimport com.viaversion.viaversion.api.minecraft.item.Item;\nimport com.viaversion.viaversion.api.minecraft.item.data.Enchantments;\nimport com.viaversion.viaversion.libs.fastutil.ints.Int2IntMap;\nimport com.viaversion.viaversion.libs.fastutil.objects.ObjectIterator;\nimport com.viaversion.viaversion.rewriter.IdRewriteFunction;\nimport com.viaversion.viaversion.util.ComponentUtil;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport static com.viaversion.viabackwards.api.rewriters.EnchantmentRewriter.ENCHANTMENT_LEVEL_TRANSLATION;\n\npublic class StructuredEnchantmentRewriter {\n\n    protected final BackwardsStructuredItemRewriter<?, ?, ?> itemRewriter;\n    private boolean rewriteIds = true;\n\n    public StructuredEnchantmentRewriter(final BackwardsStructuredItemRewriter<?, ?, ?> itemRewriter) {\n        this.itemRewriter = itemRewriter;\n    }\n\n    public void handleToClient(final Item item) {\n        final StructuredDataContainer data = item.dataContainer();\n        final BackwardsMappingData mappingData = itemRewriter.protocol().getMappingData();\n        final IdRewriteFunction idRewriteFunction = id -> {\n            final Mappings mappings = mappingData.getEnchantmentMappings();\n            return mappings.getNewId(id);\n        };\n        final DescriptionSupplier descriptionSupplier = (id, level) -> {\n            final String remappedName = mappingData.mappedEnchantmentName(id);\n            return ComponentUtil.jsonStringToTag(ChatUtil.legacyToJsonString(\"§7\" + remappedName, ENCHANTMENT_LEVEL_TRANSLATION.formatted(level), true));\n        };\n        rewriteEnchantmentsToClient(data, StructuredDataKey.ENCHANTMENTS1_20_5, idRewriteFunction, descriptionSupplier, false);\n        rewriteEnchantmentsToClient(data, StructuredDataKey.STORED_ENCHANTMENTS1_20_5, idRewriteFunction, descriptionSupplier, true);\n    }\n\n    public void handleToServer(final Item item) {\n        final StructuredDataContainer data = item.dataContainer();\n        final CompoundTag customData = data.get(StructuredDataKey.CUSTOM_DATA);\n        if (customData != null) {\n            rewriteEnchantmentsToServer(data, customData, StructuredDataKey.ENCHANTMENTS1_20_5);\n            rewriteEnchantmentsToServer(data, customData, StructuredDataKey.STORED_ENCHANTMENTS1_20_5);\n        }\n    }\n\n    public void rewriteEnchantmentsToClient(final StructuredDataContainer data, final StructuredDataKey<Enchantments> key, final IdRewriteFunction rewriteFunction, final DescriptionSupplier descriptionSupplier, final boolean storedEnchant) {\n        final Enchantments enchantments = data.get(key);\n        if (enchantments == null || enchantments.size() == 0) {\n            return;\n        }\n\n        final List<Tag> loreToAdd = new ArrayList<>();\n        boolean removedEnchantments = false;\n        boolean updatedLore = false;\n\n        final ObjectIterator<Int2IntMap.Entry> iterator = enchantments.enchantments().int2IntEntrySet().iterator();\n        final List<PendingIdChange> updatedIds = new ArrayList<>();\n        while (iterator.hasNext()) {\n            final Int2IntMap.Entry entry = iterator.next();\n            final int id = entry.getIntKey();\n            final int mappedId = rewriteFunction.rewrite(id);\n            final int level = entry.getIntValue();\n            if (mappedId != -1) {\n                if (rewriteIds) {\n                    // Update the map after to iteration to preserve the current ids before possibly saving the original, avoid CME\n                    updatedIds.add(new PendingIdChange(id, mappedId, level));\n                }\n                continue;\n            }\n\n            if (!removedEnchantments) {\n                // Backup original before doing modifications\n                final CompoundTag customData = customData(data);\n                itemRewriter.saveListTag(customData, asTag(enchantments), key.identifier());\n                removedEnchantments = true;\n            }\n\n            final Tag description = descriptionSupplier.get(id, level);\n            if (description != null && enchantments.showInTooltip()) {\n                loreToAdd.add(description);\n                updatedLore = true;\n            }\n\n            iterator.remove();\n        }\n\n        // Remove all first, then add the new ones\n        for (final PendingIdChange change : updatedIds) {\n            enchantments.remove(change.id());\n        }\n        for (final PendingIdChange change : updatedIds) {\n            enchantments.add(change.mappedId(), change.level());\n        }\n\n        if (removedEnchantments) {\n            final CompoundTag tag = customData(data);\n            if (!storedEnchant && enchantments.size() == 0) {\n                // Add glint override if there are no enchantments left\n                final Boolean glintOverride = data.get(StructuredDataKey.ENCHANTMENT_GLINT_OVERRIDE);\n                if (glintOverride != null) {\n                    tag.putBoolean(itemRewriter.nbtTagName(\"glint\"), glintOverride);\n                } else {\n                    tag.putBoolean(itemRewriter.nbtTagName(\"noglint\"), true);\n                }\n                data.set(StructuredDataKey.ENCHANTMENT_GLINT_OVERRIDE, true);\n            }\n\n            if (enchantments.showInTooltip()) {\n                tag.putBoolean(itemRewriter.nbtTagName(\"show_\" + key.identifier()), true);\n            }\n        }\n\n        if (updatedLore) {\n            // Save original lore\n            final CompoundTag tag = customData(data);\n            final Tag[] lore = data.get(StructuredDataKey.LORE);\n            if (lore != null) {\n                final List<Tag> loreList = Arrays.asList(lore);\n                itemRewriter.saveGenericTagList(tag, loreList, \"lore\");\n                loreToAdd.addAll(loreList);\n            } else {\n                tag.putBoolean(itemRewriter.nbtTagName(\"nolore\"), true);\n            }\n            data.set(StructuredDataKey.LORE, loreToAdd.toArray(new Tag[0]));\n        }\n    }\n\n    private CompoundTag customData(final StructuredDataContainer data) {\n        CompoundTag tag = data.get(StructuredDataKey.CUSTOM_DATA);\n        if (tag == null) {\n            tag = new CompoundTag();\n            data.set(StructuredDataKey.CUSTOM_DATA, tag);\n        }\n        return tag;\n    }\n\n    private ListTag<CompoundTag> asTag(final Enchantments enchantments) {\n        final ListTag<CompoundTag> listTag = new ListTag<>(CompoundTag.class);\n        for (final Int2IntMap.Entry entry : enchantments.enchantments().int2IntEntrySet()) {\n            final CompoundTag enchantment = new CompoundTag();\n            enchantment.putInt(\"id\", entry.getIntKey());\n            enchantment.putInt(\"lvl\", entry.getIntValue());\n            listTag.add(enchantment);\n        }\n        return listTag;\n    }\n\n    public void rewriteEnchantmentsToServer(final StructuredDataContainer data, final CompoundTag tag, final StructuredDataKey<Enchantments> key) {\n        final ListTag<CompoundTag> enchantmentsTag = itemRewriter.removeListTag(tag, key.identifier(), CompoundTag.class);\n        if (enchantmentsTag == null) {\n            return;\n        }\n\n        final Tag glintTag = tag.remove(itemRewriter.nbtTagName(\"glint\"));\n        if (glintTag instanceof ByteTag) {\n            data.set(StructuredDataKey.ENCHANTMENT_GLINT_OVERRIDE, ((NumberTag) glintTag).asBoolean());\n        } else if (tag.remove(itemRewriter.nbtTagName(\"noglint\")) != null) {\n            data.remove(StructuredDataKey.ENCHANTMENT_GLINT_OVERRIDE);\n        }\n\n        final List<Tag> lore = itemRewriter.removeGenericTagList(tag, \"lore\");\n        if (lore != null) {\n            data.set(StructuredDataKey.LORE, lore.toArray(new Tag[0]));\n        } else if (tag.remove(itemRewriter.nbtTagName(\"nolore\")) != null) {\n            data.remove(StructuredDataKey.LORE);\n        }\n\n        final Enchantments enchantments = new Enchantments(tag.remove(itemRewriter.nbtTagName(\"show_\" + key.identifier())) != null);\n        for (final CompoundTag enchantment : enchantmentsTag) {\n            enchantments.add(enchantment.getInt(\"id\"), enchantment.getInt(\"lvl\"));\n        }\n        data.set(key, enchantments);\n    }\n\n    public void setRewriteIds(final boolean rewriteIds) {\n        this.rewriteIds = rewriteIds;\n    }\n\n    @FunctionalInterface\n    public interface DescriptionSupplier {\n\n        Tag get(int id, int level);\n    }\n\n    private record PendingIdChange(int id, int mappedId, int level) {\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/api/rewriters/text/JsonNBTComponentRewriter.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.api.rewriters.text;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.StringTag;\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viabackwards.api.data.TranslatableMappings;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.protocol.packet.ClientboundPacketType;\nimport com.viaversion.viaversion.libs.gson.JsonObject;\nimport java.util.Map;\nimport org.checkerframework.checker.nullness.qual.Nullable;\n\npublic class JsonNBTComponentRewriter<C extends ClientboundPacketType> extends com.viaversion.viaversion.rewriter.text.JsonNBTComponentRewriter<C> implements TranslatableRewriter {\n\n    private final Map<String, String> translatables;\n\n    public JsonNBTComponentRewriter(final BackwardsProtocol<C, ?, ?, ?> protocol, final ReadType type) {\n        super(protocol, type);\n        this.translatables = TranslatableMappings.translatablesFor(protocol);\n    }\n\n    public JsonNBTComponentRewriter(final BackwardsProtocol<C, ?, ?, ?> protocol, final ReadType type, final String version) {\n        super(protocol, type);\n        this.translatables = TranslatableMappings.translatablesFor(version);\n    }\n\n    @Override\n    protected void handleTranslate(final JsonObject root, final String translate) {\n        final String newTranslate = mappedTranslationKey(translate);\n        if (newTranslate != null) {\n            root.addProperty(\"translate\", newTranslate);\n        }\n    }\n\n    @Override\n    protected void handleTranslate(final UserConnection connection, final CompoundTag parentTag, final StringTag translateTag) {\n        final String newTranslate = mappedTranslationKey(translateTag.getValue());\n        if (newTranslate != null) {\n            parentTag.put(\"translate\", new StringTag(newTranslate));\n        }\n    }\n\n    @Override\n    public @Nullable String mappedTranslationKey(final String translationKey) {\n        return translatables.get(translationKey);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/api/rewriters/text/NBTComponentRewriter.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.api.rewriters.text;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.StringTag;\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viabackwards.api.data.TranslatableMappings;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.protocol.packet.ClientboundPacketType;\nimport com.viaversion.viaversion.libs.gson.JsonObject;\nimport java.util.Map;\nimport org.checkerframework.checker.nullness.qual.Nullable;\n\npublic class NBTComponentRewriter<C extends ClientboundPacketType> extends com.viaversion.viaversion.rewriter.text.NBTComponentRewriter<C> implements TranslatableRewriter {\n\n    private final Map<String, String> translatables;\n\n    public NBTComponentRewriter(final BackwardsProtocol<C, ?, ?, ?> protocol) {\n        super(protocol);\n        this.translatables = TranslatableMappings.translatablesFor(protocol);\n    }\n\n    public NBTComponentRewriter(final BackwardsProtocol<C, ?, ?, ?> protocol, final String version) {\n        super(protocol);\n        this.translatables = TranslatableMappings.translatablesFor(version);\n    }\n\n    @Override\n    protected void handleTranslate(final JsonObject root, final String translate) {\n        final String newTranslate = mappedTranslationKey(translate);\n        if (newTranslate != null) {\n            root.addProperty(\"translate\", newTranslate);\n        }\n    }\n\n    @Override\n    protected void handleTranslate(final UserConnection connection, final CompoundTag parentTag, final StringTag translateTag) {\n        final String newTranslate = mappedTranslationKey(translateTag.getValue());\n        if (newTranslate != null) {\n            parentTag.put(\"translate\", new StringTag(newTranslate));\n        }\n    }\n\n    @Override\n    public @Nullable String mappedTranslationKey(final String translationKey) {\n        return translatables.get(translationKey);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/api/rewriters/text/TranslatableRewriter.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.api.rewriters.text;\n\nimport com.viaversion.viaversion.api.rewriter.ComponentRewriter;\nimport org.checkerframework.checker.nullness.qual.Nullable;\n\npublic interface TranslatableRewriter extends ComponentRewriter {\n\n    @Nullable\n    String mappedTranslationKey(String translationKey);\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/item/DataItemWithExtras.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.item;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.ListTag;\nimport com.viaversion.nbt.tag.StringTag;\nimport com.viaversion.viaversion.api.minecraft.item.DataItem;\nimport com.viaversion.viaversion.api.minecraft.item.Item;\nimport com.viaversion.viaversion.libs.gson.JsonElement;\nimport com.viaversion.viaversion.libs.gson.JsonParser;\nimport com.viaversion.viaversion.libs.gson.JsonPrimitive;\nimport com.viaversion.viaversion.libs.gson.JsonSyntaxException;\nimport java.util.ArrayList;\nimport java.util.List;\nimport org.checkerframework.checker.nullness.qual.Nullable;\n\n/**\n * Prevent expensive parsing/toString by checking against cached JsonElement instances, used from 1.14 to 1.20.5.\n * <p>\n * When using this, be careful not to break caching by modifying the display tag directly.\n */\npublic final class DataItemWithExtras extends DataItem {\n\n    private JsonElement name;\n    private List<JsonElement> lore;\n\n    public DataItemWithExtras(final Item from) {\n        setIdentifier(from.identifier());\n        setAmount(from.amount());\n        setData(from.data());\n        setTag(from.tag());\n\n        if (tag() == null) {\n            return;\n        }\n\n        final CompoundTag display = tag().getCompoundTag(\"display\");\n        if (display == null) {\n            return;\n        }\n\n        final StringTag name = display.getStringTag(\"Name\");\n        if (name != null) {\n            this.name = parse(name.getValue());\n        }\n\n        final ListTag<StringTag> lore = display.getListTag(\"Lore\", StringTag.class);\n        if (lore != null) {\n            this.lore = new ArrayList<>(lore.size());\n            for (int i = 0; i < lore.size(); i++) {\n                this.lore.add(parse(lore.get(i).getValue()));\n            }\n        }\n    }\n\n    public @Nullable JsonElement name() {\n        return name;\n    }\n\n    public @Nullable StringTag rawName() {\n        if (tag() == null) {\n            return null;\n        }\n        final CompoundTag display = tag().getCompoundTag(\"display\");\n        return display != null ? display.getStringTag(\"Name\") : null;\n    }\n\n    public @Nullable List<JsonElement> lore() {\n        return lore;\n    }\n\n    public @Nullable ListTag<StringTag> rawLore() {\n        if (tag() == null) {\n            return null;\n        }\n        final CompoundTag display = tag().getCompoundTag(\"display\");\n        return display != null ? display.getListTag(\"Lore\", StringTag.class) : null;\n    }\n\n    private JsonElement parse(final String value) {\n        try {\n            return JsonParser.parseString(value);\n        } catch (final JsonSyntaxException e) {\n            return new JsonPrimitive(value);\n        }\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/registration/BackwardsRegistrations.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.registration;\n\nimport com.viaversion.viaversion.api.protocol.version.ProtocolVersion;\nimport com.viaversion.viaversion.protocol.shared_registration.SharedRegistrations;\n\npublic final class BackwardsRegistrations {\n\n    private static final SharedRegistrations REGISTRATIONS = SharedRegistrations.create();\n\n    public static void apply() {\n        REGISTRATIONS.registrations()\n            .range(ProtocolVersion.v1_10, ProtocolVersion.v1_19_3, RegistryRegistrations::registerNamedSound1_10)\n            .since(ProtocolVersion.v1_14, RegistryRegistrations::registerStopSound1_14)\n\n            .register();\n    }\n\n    public static SharedRegistrations registrations() {\n        return REGISTRATIONS;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/registration/RegistryRegistrations.java",
    "content": "/*\n * This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.registration;\n\nimport com.viaversion.viabackwards.api.rewriters.SoundRewriter;\nimport com.viaversion.viaversion.api.protocol.packet.ClientboundPacketType;\nimport com.viaversion.viaversion.protocol.shared_registration.PacketBound;\nimport com.viaversion.viaversion.protocol.shared_registration.RegistrationContext;\nimport com.viaversion.viaversion.protocols.v1_13_2to1_14.packet.ClientboundPackets1_14;\nimport com.viaversion.viaversion.protocols.v1_9_1to1_9_3.packet.ClientboundPackets1_9_3;\n\nfinal class RegistryRegistrations {\n\n    static <CU extends ClientboundPacketType> void registerNamedSound1_10(final RegistrationContext<CU, ?> ctx) {\n        ctx.clientbound(ClientboundPackets1_9_3.CUSTOM_SOUND, new SoundRewriter<>(ctx.protocol())::registerNamedSound, PacketBound.REMOVED_AT_MAX);\n    }\n\n    static <CU extends ClientboundPacketType> void registerStopSound1_14(final RegistrationContext<CU, ?> ctx) {\n        ctx.clientbound(ClientboundPackets1_14.STOP_SOUND, new SoundRewriter<>(ctx.protocol())::registerStopSound);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/template/BlockItemPacketRewriter99_1.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.template;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.viabackwards.api.rewriters.BackwardsStructuredItemRewriter;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.data.StructuredDataContainer;\nimport com.viaversion.viaversion.api.minecraft.item.Item;\nimport com.viaversion.viaversion.protocols.v1_21_11to26_1.packet.ClientboundPacket26_1;\nimport com.viaversion.viaversion.protocols.v1_21_7to1_21_9.packet.ServerboundPacket1_21_9;\n\n// To replace if needed:\n//   ChunkType1_21_5\n//   RecipeDisplayRewriter\nfinal class BlockItemPacketRewriter99_1 extends BackwardsStructuredItemRewriter<ClientboundPacket26_1, ServerboundPacket1_21_9, Protocol99_1To98_1> {\n\n    public BlockItemPacketRewriter99_1(final Protocol99_1To98_1 protocol) {\n        super(protocol);\n    }\n\n    @Override\n    public void registerPackets() {\n    }\n\n    @Override\n    protected void handleItemDataComponentsToClient(final UserConnection connection, final Item item, final StructuredDataContainer container) {\n        super.handleItemDataComponentsToClient(connection, item, container);\n        // downgradeData(item, container); // static import VV method\n    }\n\n    @Override\n    protected void handleItemDataComponentsToServer(final UserConnection connection, final Item item, final StructuredDataContainer container) {\n        super.handleItemDataComponentsToServer(connection, item, container);\n        // upgradeData(item, container); // static import VV method\n    }\n\n    @Override\n    protected void restoreBackupData(final Item item, final StructuredDataContainer container, final CompoundTag customData) {\n        super.restoreBackupData(item, container, customData);\n        // restore any data if needed here\n    }\n\n    @Override\n    protected void backupInconvertibleData(final UserConnection connection, final Item item, final StructuredDataContainer dataContainer, final CompoundTag backupTag) {\n        super.backupInconvertibleData(connection, item, dataContainer, backupTag);\n        // back up any data if needed here, called before the method below\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/template/ComponentRewriter99_1.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.template;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viabackwards.api.rewriters.text.NBTComponentRewriter;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.protocols.v1_21_11to26_1.packet.ClientboundPacket26_1;\n\nfinal class ComponentRewriter99_1 extends NBTComponentRewriter<ClientboundPacket26_1> {\n\n    public ComponentRewriter99_1(final BackwardsProtocol<ClientboundPacket26_1, ?, ?, ?> protocol) {\n        super(protocol);\n    }\n\n    @Override\n    protected void handleShowItem(final UserConnection connection, final CompoundTag itemTag, final CompoundTag componentsTag) {\n        super.handleShowItem(connection, itemTag, componentsTag);\n        if (componentsTag == null) {\n            return;\n        }\n\n        // Remove or update data from componentsTag\n        // Newly added data which is not handled otherwise needs to be removed to prevent errors on the client\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/template/EntityPacketRewriter99_1.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.template;\n\nimport com.viaversion.viabackwards.api.rewriters.EntityRewriter;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityType;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_21_11;\nimport com.viaversion.viaversion.api.minecraft.entitydata.types.EntityDataTypes26_1;\nimport com.viaversion.viaversion.api.type.types.version.VersionedTypes;\nimport com.viaversion.viaversion.protocols.v1_21_11to26_1.packet.ClientboundPacket26_1;\n\n// Replace if needed\n//  VersionedTypes\nfinal class EntityPacketRewriter99_1 extends EntityRewriter<ClientboundPacket26_1, Protocol99_1To98_1> {\n\n    private static final EntityDataTypes26_1 MAPPED_DATA_TYPES = VersionedTypes.V26_1.entityDataTypes;\n\n    public EntityPacketRewriter99_1(final Protocol99_1To98_1 protocol) {\n        super(protocol, MAPPED_DATA_TYPES.optionalComponentType, MAPPED_DATA_TYPES.booleanType);\n    }\n\n    @Override\n    public void registerPackets() {\n    }\n\n    @Override\n    protected void registerRewrites() {\n        dataTypeMapper().register();\n        /* ... or like this for additions and removals that are not at the very end\n        dataTypeMapper()\n            .added(MAPPED_DATA_TYPES.catSoundVariant)\n            .removed(MAPPED_DATA_TYPES.cowSoundVariant)\n            .skip(MAPPED_DATA_TYPES.pigSoundVariant) // if neither removed nor added, but the value type has to be changed separately\n            .register();*/\n\n        registerEntityDataTypeHandler1_20_3(\n            MAPPED_DATA_TYPES.itemType,\n            MAPPED_DATA_TYPES.blockStateType,\n            MAPPED_DATA_TYPES.optionalBlockStateType,\n            MAPPED_DATA_TYPES.particleType,\n            MAPPED_DATA_TYPES.particlesType,\n            MAPPED_DATA_TYPES.componentType,\n            MAPPED_DATA_TYPES.optionalComponentType\n        );\n\n        // Remove entity data of new entity type\n        // filter().type(EntityTypes1_21_11.SNIFFER).removeIndex(newIndex);\n    }\n\n    @Override\n    public void onMappingDataLoaded() {\n        super.onMappingDataLoaded();\n        // mapEntityTypeWithData(EntityTypes1_21_11.SNIFFER, EntityTypes1_21_11.RAVAGER).tagName();\n    }\n\n    @Override\n    public EntityType typeFromId(final int type) {\n        return EntityTypes1_21_11.getTypeFromId(type);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/template/Protocol99_1To98_1.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.template;\n\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viabackwards.api.data.BackwardsMappingData;\nimport com.viaversion.viabackwards.api.rewriters.BackwardsRegistryRewriter;\nimport com.viaversion.viabackwards.api.rewriters.text.NBTComponentRewriter;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_21_11;\nimport com.viaversion.viaversion.api.protocol.packet.provider.PacketTypesProvider;\nimport com.viaversion.viaversion.api.protocol.packet.provider.SimplePacketTypesProvider;\nimport com.viaversion.viaversion.api.type.types.chunk.ChunkType26_1;\nimport com.viaversion.viaversion.api.type.types.version.VersionedTypes;\nimport com.viaversion.viaversion.api.type.types.version.VersionedTypesHolder;\nimport com.viaversion.viaversion.data.entity.EntityTrackerBase;\nimport com.viaversion.viaversion.data.item.ItemHasherBase;\nimport com.viaversion.viaversion.protocols.v1_21_11to26_1.Protocol1_21_11To26_1;\nimport com.viaversion.viaversion.protocols.v1_21_11to26_1.packet.ClientboundPacket26_1;\nimport com.viaversion.viaversion.protocols.v1_21_11to26_1.packet.ClientboundPackets26_1;\nimport com.viaversion.viaversion.protocols.v1_21_4to1_21_5.rewriter.RecipeDisplayRewriter1_21_5;\nimport com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ServerboundPackets1_21_6;\nimport com.viaversion.viaversion.protocols.v1_21_7to1_21_9.packet.ClientboundConfigurationPackets1_21_9;\nimport com.viaversion.viaversion.protocols.v1_21_7to1_21_9.packet.ServerboundConfigurationPackets1_21_9;\nimport com.viaversion.viaversion.protocols.v1_21_7to1_21_9.packet.ServerboundPacket1_21_9;\nimport com.viaversion.viaversion.rewriter.BlockRewriter;\nimport com.viaversion.viaversion.rewriter.ParticleRewriter;\nimport com.viaversion.viaversion.rewriter.RecipeDisplayRewriter;\nimport com.viaversion.viaversion.rewriter.TagRewriter;\nimport com.viaversion.viaversion.rewriter.block.BlockRewriter1_21_5;\n\nimport static com.viaversion.viaversion.util.ProtocolUtil.packetTypeMap;\n\n// Placeholders to replace (in the entire package):\n//   Protocol1_21_11To26_1 (the ViaVersion protocol class the mappings depend on)\n//   ClientboundPacket26_1\n//   ServerboundPacket1_21_9\n//   ClientboundConfigurationPackets1_21\n//   ServerboundConfigurationPackets1_20_5\n//   EntityTypes1_21_11 (UNMAPPED type)\n//   VersionedTypes.V26_1\n//   99.1, 98.1\nfinal class Protocol99_1To98_1 extends BackwardsProtocol<ClientboundPacket26_1, ClientboundPacket26_1, ServerboundPacket1_21_9, ServerboundPacket1_21_9> {\n\n    // ViaBackwards uses its own mappings and also needs a translatablerewriter for translation mappings\n    public static final BackwardsMappingData MAPPINGS = new BackwardsMappingData(\"99.1\", \"98.1\", Protocol1_21_11To26_1.class); // Change the VV (!) protocol class\n    private final EntityPacketRewriter99_1 entityRewriter = new EntityPacketRewriter99_1(this);\n    private final BlockItemPacketRewriter99_1 itemRewriter = new BlockItemPacketRewriter99_1(this);\n    private final ParticleRewriter<ClientboundPacket26_1> particleRewriter = new ParticleRewriter<>(this);\n    private final NBTComponentRewriter<ClientboundPacket26_1> translatableRewriter = new ComponentRewriter99_1(this);\n    private final TagRewriter<ClientboundPacket26_1> tagRewriter = new TagRewriter<>(this);\n    private final BackwardsRegistryRewriter registryDataRewriter = new BackwardsRegistryRewriter(this);\n    private final BlockRewriter<ClientboundPacket26_1> blockRewriter = new BlockRewriter1_21_5<>(this, ChunkType26_1::new);\n    private final RecipeDisplayRewriter<ClientboundPacket26_1> recipeRewriter = new RecipeDisplayRewriter1_21_5<>(this);\n\n    public Protocol99_1To98_1() {\n        super(ClientboundPacket26_1.class, ClientboundPacket26_1.class, ServerboundPacket1_21_9.class, ServerboundPacket1_21_9.class);\n    }\n\n    @Override\n    protected void registerPackets() {\n        super.registerPackets();\n    }\n\n    @Override\n    public void init(final UserConnection connection) {\n        addEntityTracker(connection, new EntityTrackerBase(connection, EntityTypes1_21_11.PLAYER));\n        addItemHasher(connection, new ItemHasherBase(this, connection));\n    }\n\n    @Override\n    public BackwardsMappingData getMappingData() {\n        return MAPPINGS;\n    }\n\n    @Override\n    public EntityPacketRewriter99_1 getEntityRewriter() {\n        return entityRewriter;\n    }\n\n    @Override\n    public BlockItemPacketRewriter99_1 getItemRewriter() {\n        return itemRewriter;\n    }\n\n    @Override\n    public BlockRewriter<ClientboundPacket26_1> getBlockRewriter() {\n        return blockRewriter;\n    }\n\n    @Override\n    public RecipeDisplayRewriter<ClientboundPacket26_1> getRecipeRewriter() {\n        return recipeRewriter;\n    }\n\n    @Override\n    public BackwardsRegistryRewriter getRegistryDataRewriter() {\n        return registryDataRewriter;\n    }\n\n    @Override\n    public ParticleRewriter<ClientboundPacket26_1> getParticleRewriter() {\n        return particleRewriter;\n    }\n\n    @Override\n    public NBTComponentRewriter<ClientboundPacket26_1> getComponentRewriter() {\n        return translatableRewriter;\n    }\n\n    @Override\n    public TagRewriter<ClientboundPacket26_1> getTagRewriter() {\n        return tagRewriter;\n    }\n\n    @Override\n    public VersionedTypesHolder types() {\n        return VersionedTypes.V26_1;\n    }\n\n    @Override\n    public VersionedTypesHolder mappedTypes() {\n        return VersionedTypes.V26_1;\n    }\n\n    @Override\n    protected PacketTypesProvider<ClientboundPacket26_1, ClientboundPacket26_1, ServerboundPacket1_21_9, ServerboundPacket1_21_9> createPacketTypesProvider() {\n        return new SimplePacketTypesProvider<>(\n            packetTypeMap(unmappedClientboundPacketType, ClientboundPackets26_1.class, ClientboundConfigurationPackets1_21_9.class),\n            packetTypeMap(mappedClientboundPacketType, ClientboundPackets26_1.class, ClientboundConfigurationPackets1_21_9.class),\n            packetTypeMap(mappedServerboundPacketType, ServerboundPackets1_21_6.class, ServerboundConfigurationPackets1_21_9.class),\n            packetTypeMap(unmappedServerboundPacketType, ServerboundPackets1_21_6.class, ServerboundConfigurationPackets1_21_9.class)\n        );\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_10to1_9_3/Protocol1_10To1_9_3.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.viaversion.viabackwards.protocol.v1_10to1_9_3;\n\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viabackwards.api.data.BackwardsMappingData;\nimport com.viaversion.viabackwards.api.rewriters.SoundRewriter;\nimport com.viaversion.viabackwards.api.rewriters.text.JsonNBTComponentRewriter;\nimport com.viaversion.viabackwards.protocol.v1_10to1_9_3.rewriter.BlockItemPacketRewriter1_10;\nimport com.viaversion.viabackwards.protocol.v1_10to1_9_3.rewriter.EntityPacketRewriter1_10;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.ClientWorld;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_10;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.protocol.remapper.ValueTransformer;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.data.entity.EntityTrackerBase;\nimport com.viaversion.viaversion.protocols.v1_9_1to1_9_3.packet.ClientboundPackets1_9_3;\nimport com.viaversion.viaversion.protocols.v1_9_1to1_9_3.packet.ServerboundPackets1_9_3;\nimport com.viaversion.viaversion.rewriter.text.ComponentRewriterBase;\n\npublic class Protocol1_10To1_9_3 extends BackwardsProtocol<ClientboundPackets1_9_3, ClientboundPackets1_9_3, ServerboundPackets1_9_3, ServerboundPackets1_9_3> {\n\n    public static final BackwardsMappingData MAPPINGS = new BackwardsMappingData(\"1.10\", \"1.9.4\");\n    private static final ValueTransformer<Float, Short> TO_OLD_PITCH = new ValueTransformer<>(Types.UNSIGNED_BYTE) {\n        public Short transform(PacketWrapper packetWrapper, Float inputValue) {\n            return (short) Math.round(inputValue * 63.5F);\n        }\n    };\n    private final EntityPacketRewriter1_10 entityRewriter = new EntityPacketRewriter1_10(this);\n    private final BlockItemPacketRewriter1_10 itemRewriter = new BlockItemPacketRewriter1_10(this);\n\n    public Protocol1_10To1_9_3() {\n        super(ClientboundPackets1_9_3.class, ClientboundPackets1_9_3.class, ServerboundPackets1_9_3.class, ServerboundPackets1_9_3.class);\n    }\n\n    @Override\n    protected void registerPackets() {\n        entityRewriter.register();\n        itemRewriter.register();\n\n        SoundRewriter<ClientboundPackets1_9_3> soundRewriter = new SoundRewriter<>(this);\n        replaceClientbound(ClientboundPackets1_9_3.CUSTOM_SOUND, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.STRING); // 0 - Sound name\n                map(Types.VAR_INT); // 1 - Sound Category\n                map(Types.INT); // 2 - x\n                map(Types.INT); // 3 - y\n                map(Types.INT); // 4 - z\n                map(Types.FLOAT); // 5 - Volume\n                map(Types.FLOAT, TO_OLD_PITCH); // 6 - Pitch\n                handler(soundRewriter.getNamedSoundHandler());\n            }\n        });\n        replaceClientbound(ClientboundPackets1_9_3.SOUND, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT); // 0 - Sound name\n                map(Types.VAR_INT); // 1 - Sound Category\n                map(Types.INT); // 2 - x\n                map(Types.INT); // 3 - y\n                map(Types.INT); // 4 - z\n                map(Types.FLOAT); // 5 - Volume\n                map(Types.FLOAT, TO_OLD_PITCH); // 6 - Pitch\n                handler(soundRewriter.getSoundHandler());\n            }\n        });\n\n        registerServerbound(ServerboundPackets1_9_3.RESOURCE_PACK, new PacketHandlers() {\n            @Override\n            public void register() {\n                read(Types.STRING); // 0 - Hash\n                map(Types.VAR_INT); // 1 - Result\n            }\n        });\n\n        JsonNBTComponentRewriter<ClientboundPackets1_9_3> componentRewriter = new JsonNBTComponentRewriter<>(this, ComponentRewriterBase.ReadType.JSON);\n        componentRewriter.registerComponentPacket(ClientboundPackets1_9_3.CHAT);\n    }\n\n    @Override\n    public void init(UserConnection user) {\n        user.addEntityTracker(this.getClass(), new EntityTrackerBase(user, EntityTypes1_10.EntityType.PLAYER));\n        user.addClientWorld(this.getClass(), new ClientWorld());\n    }\n\n    @Override\n    public BackwardsMappingData getMappingData() {\n        return MAPPINGS;\n    }\n\n    @Override\n    public EntityPacketRewriter1_10 getEntityRewriter() {\n        return entityRewriter;\n    }\n\n    @Override\n    public BlockItemPacketRewriter1_10 getItemRewriter() {\n        return itemRewriter;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_10to1_9_3/rewriter/BlockItemPacketRewriter1_10.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.viaversion.viabackwards.protocol.v1_10to1_9_3.rewriter;\n\nimport com.viaversion.viabackwards.api.rewriters.LegacyBlockItemRewriter;\nimport com.viaversion.viabackwards.protocol.v1_10to1_9_3.Protocol1_10To1_9_3;\nimport com.viaversion.viaversion.api.minecraft.ClientWorld;\nimport com.viaversion.viaversion.api.minecraft.chunks.Chunk;\nimport com.viaversion.viaversion.api.minecraft.item.Item;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.chunk.ChunkType1_9_3;\nimport com.viaversion.viaversion.protocols.v1_9_1to1_9_3.packet.ClientboundPackets1_9_3;\nimport com.viaversion.viaversion.protocols.v1_9_1to1_9_3.packet.ServerboundPackets1_9_3;\n\npublic class BlockItemPacketRewriter1_10 extends LegacyBlockItemRewriter<ClientboundPackets1_9_3, ServerboundPackets1_9_3, Protocol1_10To1_9_3> {\n\n    public BlockItemPacketRewriter1_10(Protocol1_10To1_9_3 protocol) {\n        super(protocol, \"1.10\");\n    }\n\n    @Override\n    protected void registerPackets() {\n        registerBlockChange(ClientboundPackets1_9_3.BLOCK_UPDATE);\n        registerMultiBlockChange(ClientboundPackets1_9_3.CHUNK_BLOCKS_UPDATE);\n\n        registerSetSlot(ClientboundPackets1_9_3.CONTAINER_SET_SLOT);\n        registerSetContent(ClientboundPackets1_9_3.CONTAINER_SET_CONTENT);\n\n        registerSetEquippedItem(ClientboundPackets1_9_3.SET_EQUIPPED_ITEM);\n        registerCustomPayloadTradeList(ClientboundPackets1_9_3.CUSTOM_PAYLOAD);\n\n        registerContainerClick(ServerboundPackets1_9_3.CONTAINER_CLICK);\n        registerSetCreativeModeSlot(ServerboundPackets1_9_3.SET_CREATIVE_MODE_SLOT);\n\n        protocol.registerClientbound(ClientboundPackets1_9_3.LEVEL_CHUNK, wrapper -> {\n            ClientWorld clientWorld = wrapper.user().getClientWorld(Protocol1_10To1_9_3.class);\n\n            ChunkType1_9_3 type = ChunkType1_9_3.forEnvironment(clientWorld.getEnvironment());\n            Chunk chunk = wrapper.passthrough(type);\n\n            handleChunk(chunk);\n        });\n\n        // Rewrite entity data items\n        protocol.getEntityRewriter().filter().handler((event, data) -> {\n            if (data.dataType().type().equals(Types.ITEM1_8)) // Is Item\n                data.setValue(handleItemToClient(event.user(), (Item) data.getValue()));\n        });\n\n        // Particle\n        protocol.registerClientbound(ClientboundPackets1_9_3.LEVEL_PARTICLES, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.INT);\n                map(Types.BOOLEAN);\n                map(Types.FLOAT);\n                map(Types.FLOAT);\n                map(Types.FLOAT);\n                map(Types.FLOAT);\n                map(Types.FLOAT);\n                map(Types.FLOAT);\n                map(Types.FLOAT);\n                map(Types.INT);\n\n                handler(wrapper -> {\n                    int id = wrapper.get(Types.INT, 0);\n                    if (id == 46) { // new falling_dust\n                        wrapper.set(Types.INT, 0, 38); // -> block_dust\n                    }\n                });\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_10to1_9_3/rewriter/EntityPacketRewriter1_10.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.viaversion.viabackwards.protocol.v1_10to1_9_3.rewriter;\n\nimport com.viaversion.viabackwards.api.entities.storage.EntityReplacement;\nimport com.viaversion.viabackwards.api.entities.storage.WrappedEntityData;\nimport com.viaversion.viabackwards.api.rewriters.LegacyEntityRewriter;\nimport com.viaversion.viabackwards.protocol.v1_10to1_9_3.Protocol1_10To1_9_3;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityType;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_10;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_11;\nimport com.viaversion.viaversion.api.minecraft.entitydata.EntityData;\nimport com.viaversion.viaversion.api.minecraft.entitydata.types.EntityDataTypes1_9;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.protocols.v1_9_1to1_9_3.packet.ClientboundPackets1_9_3;\nimport java.util.List;\n\npublic class EntityPacketRewriter1_10 extends LegacyEntityRewriter<ClientboundPackets1_9_3, Protocol1_10To1_9_3> {\n\n    public EntityPacketRewriter1_10(Protocol1_10To1_9_3 protocol) {\n        super(protocol);\n    }\n\n    @Override\n    protected void registerPackets() {\n        protocol.registerClientbound(ClientboundPackets1_9_3.ADD_ENTITY, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT); // 0 - Entity id\n                map(Types.UUID); // 1 - UUID\n                map(Types.BYTE); // 2 - Type\n                map(Types.DOUBLE); // 3 - x\n                map(Types.DOUBLE); // 4 - y\n                map(Types.DOUBLE); // 5 - z\n                map(Types.BYTE); // 6 - Pitch\n                map(Types.BYTE); // 7 - Yaw\n                map(Types.INT); // 8 - data\n\n                // Track Entity\n                handler(getObjectTrackerHandler());\n                handler(getObjectRewriter(EntityTypes1_11.ObjectType::findById));\n\n                handler(protocol.getItemRewriter().getFallingBlockHandler());\n            }\n        });\n\n        registerTracker(ClientboundPackets1_9_3.ADD_EXPERIENCE_ORB, EntityTypes1_10.EntityType.EXPERIENCE_ORB);\n        registerTracker(ClientboundPackets1_9_3.ADD_GLOBAL_ENTITY, EntityTypes1_10.EntityType.LIGHTNING_BOLT);\n\n        protocol.registerClientbound(ClientboundPackets1_9_3.ADD_MOB, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT); // 0 - Entity id\n                map(Types.UUID); // 1 - UUID\n                map(Types.UNSIGNED_BYTE); // 2 - Entity Type\n                map(Types.DOUBLE); // 3 - X\n                map(Types.DOUBLE); // 4 - Y\n                map(Types.DOUBLE); // 5 - Z\n                map(Types.BYTE); // 6 - Yaw\n                map(Types.BYTE); // 7 - Pitch\n                map(Types.BYTE); // 8 - Head Pitch\n                map(Types.SHORT); // 9 - Velocity X\n                map(Types.SHORT); // 10 - Velocity Y\n                map(Types.SHORT); // 11 - Velocity Z\n                map(Types.ENTITY_DATA_LIST1_9); // 12 - Entity data\n\n                // Track entity\n                handler(getTrackerHandler(Types.UNSIGNED_BYTE, 0));\n\n                // Rewrite entity type / data\n                handler(wrapper -> {\n                    int entityId = wrapper.get(Types.VAR_INT, 0);\n                    EntityType type = tracker(wrapper.user()).entityType(entityId);\n                    if (type == null) {\n                        return;\n                    }\n\n                    List<EntityData> entityDataList = wrapper.get(Types.ENTITY_DATA_LIST1_9, 0);\n                    handleEntityData(wrapper.get(Types.VAR_INT, 0), entityDataList, wrapper.user());\n\n                    EntityReplacement entityReplacement = entityDataForType(type);\n                    if (entityReplacement != null) {\n                        WrappedEntityData storage = new WrappedEntityData(entityDataList);\n                        wrapper.set(Types.UNSIGNED_BYTE, 0, (short) entityReplacement.replacementId());\n                        if (entityReplacement.hasBaseData())\n                            entityReplacement.defaultData().createData(storage);\n                    }\n                });\n\n            }\n        });\n\n        registerTracker(ClientboundPackets1_9_3.ADD_PAINTING, EntityTypes1_10.EntityType.PAINTING);\n        registerJoinGame(ClientboundPackets1_9_3.LOGIN, EntityTypes1_10.EntityType.PLAYER);\n        registerRespawn(ClientboundPackets1_9_3.RESPAWN);\n\n        protocol.registerClientbound(ClientboundPackets1_9_3.ADD_PLAYER, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT); // 0 - Entity ID\n                map(Types.UUID); // 1 - Player UUID\n                map(Types.DOUBLE); // 2 - X\n                map(Types.DOUBLE); // 3 - Y\n                map(Types.DOUBLE); // 4 - Z\n                map(Types.BYTE); // 5 - Yaw\n                map(Types.BYTE); // 6 - Pitch\n                map(Types.ENTITY_DATA_LIST1_9); // 7 - Entity data list\n\n                handler(getTrackerAndDataHandler(Types.ENTITY_DATA_LIST1_9, EntityTypes1_11.EntityType.PLAYER));\n            }\n        });\n\n        registerRemoveEntities(ClientboundPackets1_9_3.REMOVE_ENTITIES);\n        registerSetEntityData(ClientboundPackets1_9_3.SET_ENTITY_DATA, Types.ENTITY_DATA_LIST1_9);\n    }\n\n    @Override\n    protected void registerRewrites() {\n        mapEntityTypeWithData(EntityTypes1_10.EntityType.POLAR_BEAR, EntityTypes1_10.EntityType.SHEEP).plainName();\n\n        // Change the sheep color when the polar bear is standing up (index 13 -> Standing up)\n        filter().type(EntityTypes1_10.EntityType.POLAR_BEAR).index(13).handler((event, data) -> {\n            boolean b = (boolean) data.getValue();\n\n            data.setTypeAndValue(EntityDataTypes1_9.BYTE, b ? (byte) (14 & 0x0F) : (byte) (0));\n        });\n\n\n        // Handle husk (index 13 -> Zombie Type)\n        filter().type(EntityTypes1_10.EntityType.ZOMBIE).index(13).handler((event, data) -> {\n            if ((int) data.getValue() == 6) { // Is type Husk\n                data.setValue(0);\n            }\n        });\n\n        // Handle Stray (index 12 -> Skeleton Type)\n        filter().type(EntityTypes1_10.EntityType.SKELETON).index(12).handler((event, data) -> {\n            if ((int) data.getValue() == 2) {\n                data.setValue(0); // Change to default skeleton\n            }\n        });\n\n        // Added index from incorrect entity data hierarchy, where potions add to item entities\n        filter().type(EntityTypes1_10.EntityType.POTION).addIndex(6);\n\n        // Handle the missing NoGravity tag for every entity data\n        filter().removeIndex(5);\n    }\n\n    @Override\n    public EntityType typeFromId(int typeId) {\n        return EntityTypes1_10.EntityType.findById(typeId);\n    }\n\n    @Override\n    public EntityType objectTypeFromId(int typeId, int data) {\n        return EntityTypes1_10.ObjectType.getEntityType(typeId, data);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_11_1to1_11/Protocol1_11_1To1_11.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.viaversion.viabackwards.protocol.v1_11_1to1_11;\n\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viabackwards.protocol.v1_11_1to1_11.rewriter.EntityPacketRewriter1_11_1;\nimport com.viaversion.viabackwards.protocol.v1_11_1to1_11.rewriter.ItemPacketRewriter1_11_1;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.ClientWorld;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_11;\nimport com.viaversion.viaversion.data.entity.EntityTrackerBase;\nimport com.viaversion.viaversion.protocols.v1_9_1to1_9_3.packet.ClientboundPackets1_9_3;\nimport com.viaversion.viaversion.protocols.v1_9_1to1_9_3.packet.ServerboundPackets1_9_3;\n\npublic class Protocol1_11_1To1_11 extends BackwardsProtocol<ClientboundPackets1_9_3, ClientboundPackets1_9_3, ServerboundPackets1_9_3, ServerboundPackets1_9_3> {\n\n    private final EntityPacketRewriter1_11_1 entityRewriter = new EntityPacketRewriter1_11_1(this);\n    private final ItemPacketRewriter1_11_1 itemRewriter = new ItemPacketRewriter1_11_1(this);\n\n    public Protocol1_11_1To1_11() {\n        super(ClientboundPackets1_9_3.class, ClientboundPackets1_9_3.class, ServerboundPackets1_9_3.class, ServerboundPackets1_9_3.class);\n    }\n\n    @Override\n    public void init(UserConnection user) {\n        user.addEntityTracker(this.getClass(), new EntityTrackerBase(user, EntityTypes1_11.EntityType.PLAYER));\n        user.addClientWorld(this.getClass(), new ClientWorld());\n    }\n\n    @Override\n    public EntityPacketRewriter1_11_1 getEntityRewriter() {\n        return entityRewriter;\n    }\n\n    @Override\n    public ItemPacketRewriter1_11_1 getItemRewriter() {\n        return itemRewriter;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_11_1to1_11/rewriter/EntityPacketRewriter1_11_1.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.viaversion.viabackwards.protocol.v1_11_1to1_11.rewriter;\n\nimport com.viaversion.viabackwards.api.rewriters.LegacyEntityRewriter;\nimport com.viaversion.viabackwards.protocol.v1_11_1to1_11.Protocol1_11_1To1_11;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityType;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_11;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.protocols.v1_9_1to1_9_3.packet.ClientboundPackets1_9_3;\n\npublic class EntityPacketRewriter1_11_1 extends LegacyEntityRewriter<ClientboundPackets1_9_3, Protocol1_11_1To1_11> {\n\n    public EntityPacketRewriter1_11_1(Protocol1_11_1To1_11 protocol) {\n        super(protocol);\n    }\n\n    @Override\n    protected void registerPackets() {\n        protocol.registerClientbound(ClientboundPackets1_9_3.ADD_ENTITY, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT); // 0 - Entity id\n                map(Types.UUID); // 1 - UUID\n                map(Types.BYTE); // 2 - Type\n                map(Types.DOUBLE); // 3 - x\n                map(Types.DOUBLE); // 4 - y\n                map(Types.DOUBLE); // 5 - z\n                map(Types.BYTE); // 6 - Pitch\n                map(Types.BYTE); // 7 - Yaw\n                map(Types.INT); // 8 - data\n\n                // Track Entity\n                handler(getObjectTrackerHandler());\n                handler(getObjectRewriter(EntityTypes1_11.ObjectType::findById));\n            }\n        });\n\n        registerTracker(ClientboundPackets1_9_3.ADD_EXPERIENCE_ORB, EntityTypes1_11.EntityType.EXPERIENCE_ORB);\n        registerTracker(ClientboundPackets1_9_3.ADD_GLOBAL_ENTITY, EntityTypes1_11.EntityType.LIGHTNING_BOLT);\n\n        protocol.registerClientbound(ClientboundPackets1_9_3.ADD_MOB, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT); // 0 - Entity id\n                map(Types.UUID); // 1 - UUID\n                map(Types.VAR_INT); // 2 - Entity Type\n                map(Types.DOUBLE); // 3 - X\n                map(Types.DOUBLE); // 4 - Y\n                map(Types.DOUBLE); // 5 - Z\n                map(Types.BYTE); // 6 - Yaw\n                map(Types.BYTE); // 7 - Pitch\n                map(Types.BYTE); // 8 - Head Pitch\n                map(Types.SHORT); // 9 - Velocity X\n                map(Types.SHORT); // 10 - Velocity Y\n                map(Types.SHORT); // 11 - Velocity Z\n                map(Types.ENTITY_DATA_LIST1_9); // 12 - Entity data\n\n                // Track entity\n                handler(getTrackerHandler());\n\n                // Rewrite entity type / data\n                handler(getMobSpawnRewriter1_11(Types.ENTITY_DATA_LIST1_9));\n            }\n        });\n\n        registerTracker(ClientboundPackets1_9_3.ADD_PAINTING, EntityTypes1_11.EntityType.PAINTING);\n        registerJoinGame(ClientboundPackets1_9_3.LOGIN, EntityTypes1_11.EntityType.PLAYER);\n        registerRespawn(ClientboundPackets1_9_3.RESPAWN);\n\n        protocol.registerClientbound(ClientboundPackets1_9_3.ADD_PLAYER, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT); // 0 - Entity ID\n                map(Types.UUID); // 1 - Player UUID\n                map(Types.DOUBLE); // 2 - X\n                map(Types.DOUBLE); // 3 - Y\n                map(Types.DOUBLE); // 4 - Z\n                map(Types.BYTE); // 5 - Yaw\n                map(Types.BYTE); // 6 - Pitch\n                map(Types.ENTITY_DATA_LIST1_9); // 7 - Entity data list\n\n                handler(getTrackerAndDataHandler(Types.ENTITY_DATA_LIST1_9, EntityTypes1_11.EntityType.PLAYER));\n            }\n        });\n\n        registerRemoveEntities(ClientboundPackets1_9_3.REMOVE_ENTITIES);\n        registerSetEntityData(ClientboundPackets1_9_3.SET_ENTITY_DATA, Types.ENTITY_DATA_LIST1_9);\n    }\n\n    @Override\n    protected void registerRewrites() {\n        // Handle non-existing firework entity data (index 7 entity id for boosting)\n        filter().type(EntityTypes1_11.EntityType.FIREWORK_ROCKET).cancel(7);\n\n        // Handle non-existing pig entity data (index 14 - boost time)\n        filter().type(EntityTypes1_11.EntityType.PIG).cancel(14);\n    }\n\n    @Override\n    public EntityType typeFromId(int typeId) {\n        return EntityTypes1_11.EntityType.findById(typeId);\n    }\n\n    @Override\n    public EntityType objectTypeFromId(int typeId, int data) {\n        return EntityTypes1_11.ObjectType.getEntityType(typeId, data);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_11_1to1_11/rewriter/ItemPacketRewriter1_11_1.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.viaversion.viabackwards.protocol.v1_11_1to1_11.rewriter;\n\nimport com.viaversion.viabackwards.api.rewriters.LegacyBlockItemRewriter;\nimport com.viaversion.viabackwards.api.rewriters.LegacyEnchantmentRewriter;\nimport com.viaversion.viabackwards.protocol.v1_11_1to1_11.Protocol1_11_1To1_11;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.item.Item;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.protocols.v1_9_1to1_9_3.packet.ClientboundPackets1_9_3;\nimport com.viaversion.viaversion.protocols.v1_9_1to1_9_3.packet.ServerboundPackets1_9_3;\n\npublic class ItemPacketRewriter1_11_1 extends LegacyBlockItemRewriter<ClientboundPackets1_9_3, ServerboundPackets1_9_3, Protocol1_11_1To1_11> {\n\n    private LegacyEnchantmentRewriter enchantmentRewriter;\n\n    public ItemPacketRewriter1_11_1(Protocol1_11_1To1_11 protocol) {\n        super(protocol, \"1.11.1\");\n    }\n\n    @Override\n    protected void registerPackets() {\n        registerSetSlot(ClientboundPackets1_9_3.CONTAINER_SET_SLOT);\n        registerSetContent(ClientboundPackets1_9_3.CONTAINER_SET_CONTENT);\n        registerSetEquippedItem(ClientboundPackets1_9_3.SET_EQUIPPED_ITEM);\n        registerCustomPayloadTradeList(ClientboundPackets1_9_3.CUSTOM_PAYLOAD);\n\n        registerContainerClick(ServerboundPackets1_9_3.CONTAINER_CLICK);\n        registerSetCreativeModeSlot(ServerboundPackets1_9_3.SET_CREATIVE_MODE_SLOT);\n\n        // Handle item entity data\n        protocol.getEntityRewriter().filter().handler((event, data) -> {\n            if (data.dataType().type().equals(Types.ITEM1_8)) { // Is Item\n                data.setValue(handleItemToClient(event.user(), (Item) data.getValue()));\n            }\n        });\n    }\n\n    @Override\n    protected void registerRewrites() {\n        enchantmentRewriter = new LegacyEnchantmentRewriter(nbtTagName());\n        enchantmentRewriter.registerEnchantment(22, \"§7Sweeping Edge\");\n    }\n\n    @Override\n    public Item handleItemToClient(UserConnection connection, Item item) {\n        if (item == null) return null;\n        super.handleItemToClient(connection, item);\n\n        enchantmentRewriter.handleToClient(item);\n        return item;\n    }\n\n    @Override\n    public Item handleItemToServer(UserConnection connection, Item item) {\n        if (item == null) return null;\n        item = super.handleItemToServer(connection, item);\n\n        enchantmentRewriter.handleToServer(item);\n        return item;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_11to1_10/Protocol1_11To1_10.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.viaversion.viabackwards.protocol.v1_11to1_10;\n\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viabackwards.api.data.BackwardsMappingData;\nimport com.viaversion.viabackwards.api.rewriters.text.JsonNBTComponentRewriter;\nimport com.viaversion.viabackwards.protocol.v1_11to1_10.rewriter.BlockItemPacketRewriter1_11;\nimport com.viaversion.viabackwards.protocol.v1_11to1_10.rewriter.EntityPacketRewriter1_11;\nimport com.viaversion.viabackwards.protocol.v1_11to1_10.rewriter.PlayerPacketRewriter1_11;\nimport com.viaversion.viabackwards.protocol.v1_11to1_10.storage.WindowTracker;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.ClientWorld;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_11;\nimport com.viaversion.viaversion.data.entity.EntityTrackerBase;\nimport com.viaversion.viaversion.protocols.v1_9_1to1_9_3.packet.ClientboundPackets1_9_3;\nimport com.viaversion.viaversion.protocols.v1_9_1to1_9_3.packet.ServerboundPackets1_9_3;\nimport com.viaversion.viaversion.rewriter.text.ComponentRewriterBase;\n\npublic class Protocol1_11To1_10 extends BackwardsProtocol<ClientboundPackets1_9_3, ClientboundPackets1_9_3, ServerboundPackets1_9_3, ServerboundPackets1_9_3> {\n\n    public static final BackwardsMappingData MAPPINGS = new BackwardsMappingData(\"1.11\", \"1.10\");\n    private final EntityPacketRewriter1_11 entityRewriter = new EntityPacketRewriter1_11(this);\n    private final BlockItemPacketRewriter1_11 itemRewriter = new BlockItemPacketRewriter1_11(this);\n    private JsonNBTComponentRewriter<ClientboundPackets1_9_3> componentRewriter;\n\n    public Protocol1_11To1_10() {\n        super(ClientboundPackets1_9_3.class, ClientboundPackets1_9_3.class, ServerboundPackets1_9_3.class, ServerboundPackets1_9_3.class);\n    }\n\n    @Override\n    protected void registerPackets() {\n        entityRewriter.register();\n        itemRewriter.register();\n        PlayerPacketRewriter1_11.register(this);\n\n        componentRewriter = new JsonNBTComponentRewriter<>(this, ComponentRewriterBase.ReadType.JSON);\n        componentRewriter.registerComponentPacket(ClientboundPackets1_9_3.CHAT);\n    }\n\n    @Override\n    public void init(UserConnection user) {\n        user.addEntityTracker(this.getClass(), new EntityTrackerBase(user, EntityTypes1_11.EntityType.PLAYER));\n        user.addClientWorld(this.getClass(), new ClientWorld());\n\n        if (!user.has(WindowTracker.class)) {\n            user.put(new WindowTracker());\n        }\n    }\n\n    @Override\n    public BackwardsMappingData getMappingData() {\n        return MAPPINGS;\n    }\n\n    @Override\n    public EntityPacketRewriter1_11 getEntityRewriter() {\n        return entityRewriter;\n    }\n\n    @Override\n    public BlockItemPacketRewriter1_11 getItemRewriter() {\n        return itemRewriter;\n    }\n\n    @Override\n    public JsonNBTComponentRewriter<ClientboundPackets1_9_3> getComponentRewriter() {\n        return componentRewriter;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_11to1_10/data/SplashPotionMappings1_10.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_11to1_10.data;\n\nimport com.viaversion.viaversion.libs.fastutil.ints.Int2IntMap;\nimport com.viaversion.viaversion.libs.fastutil.ints.Int2IntOpenHashMap;\n\npublic class SplashPotionMappings1_10 {\n\n    private static final Int2IntMap DATA = new Int2IntOpenHashMap(14, 0.99F);\n\n    static {\n        DATA.defaultReturnValue(-1);\n        DATA.put(2039713, 5); // night vision\n        DATA.put(8356754, 7); // invisibility\n        DATA.put(2293580, 9); // jump boost\n        DATA.put(14981690, 12); // fire resistance\n        DATA.put(8171462, 14); // swiftness\n        DATA.put(5926017, 17); // slowness\n        DATA.put(3035801, 19); // water breathing\n        DATA.put(16262179, 21); // instant health\n        DATA.put(4393481, 23); // instant damage\n        DATA.put(5149489, 25); // poison\n        DATA.put(13458603, 28); // regeneration\n        DATA.put(9643043, 31); // strength\n        DATA.put(4738376, 34); // weakness\n        DATA.put(3381504, 36); // luck\n    }\n\n    public static int getOldData(int data) {\n        return DATA.get(data);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_11to1_10/rewriter/BlockItemPacketRewriter1_11.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.viaversion.viabackwards.protocol.v1_11to1_10.rewriter;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.StringTag;\nimport com.viaversion.viabackwards.api.data.MappedLegacyBlockItem;\nimport com.viaversion.viabackwards.api.rewriters.LegacyBlockItemRewriter;\nimport com.viaversion.viabackwards.api.rewriters.LegacyEnchantmentRewriter;\nimport com.viaversion.viabackwards.protocol.v1_11to1_10.Protocol1_11To1_10;\nimport com.viaversion.viabackwards.protocol.v1_11to1_10.storage.ChestedHorseStorage;\nimport com.viaversion.viabackwards.protocol.v1_11to1_10.storage.WindowTracker;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.data.entity.EntityTracker;\nimport com.viaversion.viaversion.api.data.entity.StoredEntityData;\nimport com.viaversion.viaversion.api.minecraft.ClientWorld;\nimport com.viaversion.viaversion.api.minecraft.chunks.Chunk;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_11;\nimport com.viaversion.viaversion.api.minecraft.item.DataItem;\nimport com.viaversion.viaversion.api.minecraft.item.Item;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.chunk.ChunkType1_9_3;\nimport com.viaversion.viaversion.protocols.v1_10to1_11.data.EntityMappings1_11;\nimport com.viaversion.viaversion.protocols.v1_9_1to1_9_3.packet.ClientboundPackets1_9_3;\nimport com.viaversion.viaversion.protocols.v1_9_1to1_9_3.packet.ServerboundPackets1_9_3;\nimport com.viaversion.viaversion.util.IdAndData;\nimport java.util.Arrays;\nimport java.util.Optional;\n\npublic class BlockItemPacketRewriter1_11 extends LegacyBlockItemRewriter<ClientboundPackets1_9_3, ServerboundPackets1_9_3, Protocol1_11To1_10> {\n\n    private LegacyEnchantmentRewriter enchantmentRewriter;\n\n    public BlockItemPacketRewriter1_11(Protocol1_11To1_10 protocol) {\n        super(protocol, \"1.11\");\n    }\n\n    @Override\n    protected void registerPackets() {\n        registerBlockChange(ClientboundPackets1_9_3.BLOCK_UPDATE);\n        registerMultiBlockChange(ClientboundPackets1_9_3.CHUNK_BLOCKS_UPDATE);\n\n        protocol.registerClientbound(ClientboundPackets1_9_3.CONTAINER_SET_SLOT, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.BYTE); // 0 - Window ID\n                map(Types.SHORT); // 1 - Slot ID\n                map(Types.ITEM1_8); // 2 - Slot Value\n\n                handler(wrapper -> handleItemToClient(wrapper.user(), wrapper.get(Types.ITEM1_8, 0)));\n\n                // Handle Llama\n                handler(wrapper -> {\n                    if (isLlama(wrapper.user())) {\n                        Optional<ChestedHorseStorage> horse = getChestedHorse(wrapper.user());\n                        if (horse.isEmpty()) {\n                            return;\n                        }\n\n                        ChestedHorseStorage storage = horse.get();\n                        int currentSlot = wrapper.get(Types.SHORT, 0);\n                        wrapper.set(Types.SHORT, 0, ((Integer) (currentSlot = getNewSlotId(storage, currentSlot))).shortValue());\n                        wrapper.set(Types.ITEM1_8, 0, getNewItem(storage, currentSlot, wrapper.get(Types.ITEM1_8, 0)));\n                    }\n                });\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_9_3.CONTAINER_SET_CONTENT, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.UNSIGNED_BYTE); // 0 - Window ID\n                map(Types.ITEM1_8_SHORT_ARRAY); // 1 - Window Values\n\n                handler(wrapper -> {\n                    Item[] stacks = wrapper.get(Types.ITEM1_8_SHORT_ARRAY, 0);\n                    for (int i = 0; i < stacks.length; i++)\n                        stacks[i] = handleItemToClient(wrapper.user(), stacks[i]);\n\n                    if (isLlama(wrapper.user())) {\n                        Optional<ChestedHorseStorage> horse = getChestedHorse(wrapper.user());\n                        if (horse.isEmpty()) {\n                            return;\n                        }\n                        ChestedHorseStorage storage = horse.get();\n                        stacks = Arrays.copyOf(stacks, !storage.isChested() ? 38 : 53);\n\n                        for (int i = stacks.length - 1; i >= 0; i--) {\n                            stacks[getNewSlotId(storage, i)] = stacks[i];\n                            stacks[i] = getNewItem(storage, i, stacks[i]);\n                        }\n                        wrapper.set(Types.ITEM1_8_SHORT_ARRAY, 0, stacks);\n                    }\n                });\n            }\n        });\n\n        registerSetEquippedItem(ClientboundPackets1_9_3.SET_EQUIPPED_ITEM);\n        registerCustomPayloadTradeList(ClientboundPackets1_9_3.CUSTOM_PAYLOAD);\n\n        protocol.registerServerbound(ServerboundPackets1_9_3.CONTAINER_CLICK, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.BYTE); // 0 - Window ID\n                map(Types.SHORT); // 1 - Slot\n                map(Types.BYTE); // 2 - Button\n                map(Types.SHORT); // 3 - Action number\n                map(Types.VAR_INT); // 4 - Mode\n                map(Types.ITEM1_8); // 5 - Clicked Item\n\n                handler(wrapper -> handleItemToServer(wrapper.user(), wrapper.get(Types.ITEM1_8, 0)));\n\n                // Llama slot\n                handler(wrapper -> {\n                    if (isLlama(wrapper.user())) {\n                        Optional<ChestedHorseStorage> horse = getChestedHorse(wrapper.user());\n                        if (horse.isEmpty()) {\n                            return;\n                        }\n                        ChestedHorseStorage storage = horse.get();\n                        int clickSlot = wrapper.get(Types.SHORT, 0);\n                        int correctSlot = getOldSlotId(storage, clickSlot);\n\n                        wrapper.set(Types.SHORT, 0, ((Integer) correctSlot).shortValue());\n                    }\n                });\n            }\n        });\n\n        registerSetCreativeModeSlot(ServerboundPackets1_9_3.SET_CREATIVE_MODE_SLOT);\n\n        protocol.registerClientbound(ClientboundPackets1_9_3.LEVEL_CHUNK, wrapper -> {\n            ClientWorld clientWorld = wrapper.user().getClientWorld(Protocol1_11To1_10.class);\n\n            ChunkType1_9_3 type = ChunkType1_9_3.forEnvironment(clientWorld.getEnvironment()); // Use the 1.10 Chunk type since nothing changed.\n            Chunk chunk = wrapper.passthrough(type);\n\n            handleChunk(chunk);\n\n            // only patch it for signs for now\n            for (CompoundTag tag : chunk.getBlockEntities()) {\n                StringTag idTag = tag.getStringTag(\"id\");\n                if (idTag == null) continue;\n\n                String id = idTag.getValue();\n                if (id.equals(\"minecraft:sign\")) {\n                    idTag.setValue(\"Sign\");\n                }\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_9_3.BLOCK_ENTITY_DATA, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.BLOCK_POSITION1_8); // 0 - Position\n                map(Types.UNSIGNED_BYTE); // 1 - Action\n                map(Types.NAMED_COMPOUND_TAG); // 2 - NBT\n\n                handler(wrapper -> {\n                    // Remove on shulkerbox decleration\n                    if (wrapper.get(Types.UNSIGNED_BYTE, 0) == 10) {\n                        wrapper.cancel();\n                    }\n                    // Handler Spawners\n                    if (wrapper.get(Types.UNSIGNED_BYTE, 0) == 1) {\n                        CompoundTag tag = wrapper.get(Types.NAMED_COMPOUND_TAG, 0);\n                        EntityMappings1_11.toClientSpawner(tag, true);\n                    }\n                });\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_9_3.OPEN_SCREEN, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.UNSIGNED_BYTE); // 0 - Window ID\n                map(Types.STRING); // 1 - Window Type\n                map(Types.COMPONENT); // 2 - Title\n                map(Types.UNSIGNED_BYTE); // 3 - Slots\n\n                handler(wrapper -> {\n                    int entityId = -1;\n                    // Passthrough Entity ID\n                    if (wrapper.get(Types.STRING, 0).equals(\"EntityHorse\")) {\n                        entityId = wrapper.passthrough(Types.INT);\n                    }\n                    // Rewrite window title\n                    protocol.getComponentRewriter().processText(wrapper.user(), wrapper.get(Types.COMPONENT, 0));\n\n                    // Track Inventory\n                    String inventory = wrapper.get(Types.STRING, 0);\n                    WindowTracker windowTracker = wrapper.user().get(WindowTracker.class);\n                    windowTracker.setInventory(inventory);\n                    windowTracker.setEntityId(entityId);\n\n                    // Change llama slotcount to the donkey one\n                    if (isLlama(wrapper.user())) {\n                        wrapper.set(Types.UNSIGNED_BYTE, 1, (short) 17);\n                    }\n                });\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_9_3.CONTAINER_CLOSE, new PacketHandlers() {\n            @Override\n            public void register() {\n                // Inventory tracking\n                handler(wrapper -> {\n                    WindowTracker windowTracker = wrapper.user().get(WindowTracker.class);\n                    windowTracker.setInventory(null);\n                    windowTracker.setEntityId(-1);\n                });\n            }\n        });\n\n\n        protocol.registerServerbound(ServerboundPackets1_9_3.CONTAINER_CLOSE, new PacketHandlers() {\n            @Override\n            public void register() {\n                // Inventory tracking\n                handler(wrapper -> {\n                    WindowTracker windowTracker = wrapper.user().get(WindowTracker.class);\n                    windowTracker.setInventory(null);\n                    windowTracker.setEntityId(-1);\n                });\n            }\n        });\n\n        protocol.getEntityRewriter().filter().handler((event, data) -> {\n            if (data.dataType().type().equals(Types.ITEM1_8)) // Is Item\n                data.setValue(handleItemToClient(event.user(), (Item) data.getValue()));\n        });\n    }\n\n    @Override\n    protected void registerRewrites() {\n        // Handle spawner block entity (map to itself with custom handler)\n        MappedLegacyBlockItem data = itemReplacements.computeIfAbsent(IdAndData.toRawData(52), s -> new MappedLegacyBlockItem(52));\n        data.setBlockEntityHandler((b, tag) -> EntityMappings1_11.toClientSpawner(tag, true));\n\n        enchantmentRewriter = new LegacyEnchantmentRewriter(nbtTagName());\n        enchantmentRewriter.registerEnchantment(71, \"§cCurse of Vanishing\");\n        enchantmentRewriter.registerEnchantment(10, \"§cCurse of Binding\");\n\n        enchantmentRewriter.setHideLevelForEnchants(71, 10); // Curses do not display their level\n    }\n\n    @Override\n    public Item handleItemToClient(UserConnection connection, Item item) {\n        if (item == null) return null;\n        super.handleItemToClient(connection, item);\n\n        CompoundTag tag = item.tag();\n        if (tag == null) return item;\n\n        // Rewrite spawn eggs (id checks are done in the method itself)\n        EntityMappings1_11.toClientItem(item, true);\n\n        enchantmentRewriter.handleToClient(item);\n        return item;\n    }\n\n    @Override\n    public Item handleItemToServer(UserConnection connection, Item item) {\n        if (item == null) return null;\n        item = super.handleItemToServer(connection, item);\n\n        CompoundTag tag = item.tag();\n        if (tag == null) return item;\n\n        // Rewrite spawn eggs (id checks are done in the method itself)\n        EntityMappings1_11.toServerItem(item, true);\n\n        enchantmentRewriter.handleToServer(item);\n        return item;\n    }\n\n    private boolean isLlama(UserConnection user) {\n        WindowTracker tracker = user.get(WindowTracker.class);\n        if (tracker.getInventory() != null && tracker.getInventory().equals(\"EntityHorse\")) {\n            EntityTracker entTracker = user.getEntityTracker(Protocol1_11To1_10.class);\n            StoredEntityData entityData = entTracker.entityData(tracker.getEntityId());\n            return entityData != null && entityData.type().is(EntityTypes1_11.EntityType.LLAMA);\n        }\n        return false;\n    }\n\n    private Optional<ChestedHorseStorage> getChestedHorse(UserConnection user) {\n        WindowTracker tracker = user.get(WindowTracker.class);\n        if (tracker.getInventory() != null && tracker.getInventory().equals(\"EntityHorse\")) {\n            EntityTracker entTracker = user.getEntityTracker(Protocol1_11To1_10.class);\n            StoredEntityData entityData = entTracker.entityData(tracker.getEntityId());\n            if (entityData != null)\n                return Optional.of(entityData.get(ChestedHorseStorage.class));\n        }\n        return Optional.empty();\n    }\n\n    private int getNewSlotId(ChestedHorseStorage storage, int slotId) {\n        int totalSlots = !storage.isChested() ? 38 : 53;\n        int strength = storage.isChested() ? storage.getLiamaStrength() : 0;\n        int startNonExistingFormula = 2 + 3 * strength;\n        int offsetForm = 15 - (3 * strength);\n\n        if (slotId >= startNonExistingFormula && totalSlots > (slotId + offsetForm))\n            return offsetForm + slotId;\n        if (slotId == 1)\n            return 0;\n        return slotId;\n    }\n\n    private int getOldSlotId(ChestedHorseStorage storage, int slotId) {\n        int strength = storage.isChested() ? storage.getLiamaStrength() : 0;\n        int startNonExistingFormula = 2 + 3 * strength;\n        int endNonExistingFormula = 2 + 3 * (storage.isChested() ? 5 : 0);\n        int offsetForm = endNonExistingFormula - startNonExistingFormula;\n\n        if (slotId == 1 || slotId >= startNonExistingFormula && slotId < endNonExistingFormula)\n            return 0;\n        if (slotId >= endNonExistingFormula)\n            return slotId - offsetForm;\n        if (slotId == 0)\n            return 1;\n        return slotId;\n    }\n\n    private Item getNewItem(ChestedHorseStorage storage, int slotId, Item current) {\n        int strength = storage.isChested() ? storage.getLiamaStrength() : 0;\n        int startNonExistingFormula = 2 + 3 * strength;\n        int endNonExistingFormula = 2 + 3 * (storage.isChested() ? 5 : 0);\n\n        if (slotId >= startNonExistingFormula && slotId < endNonExistingFormula)\n            return new DataItem(166, (byte) 1, (short) 0, getNamedTag(\"§4SLOT DISABLED\"));\n        if (slotId == 1)\n            return null;\n        return current;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_11to1_10/rewriter/EntityPacketRewriter1_11.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.viaversion.viabackwards.protocol.v1_11to1_10.rewriter;\n\nimport com.viaversion.viabackwards.api.entities.storage.WrappedEntityData;\nimport com.viaversion.viabackwards.api.rewriters.LegacyEntityRewriter;\nimport com.viaversion.viabackwards.protocol.v1_11to1_10.Protocol1_11To1_10;\nimport com.viaversion.viabackwards.protocol.v1_11to1_10.data.SplashPotionMappings1_10;\nimport com.viaversion.viabackwards.protocol.v1_11to1_10.storage.ChestedHorseStorage;\nimport com.viaversion.viaversion.api.data.entity.StoredEntityData;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityType;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_11;\nimport com.viaversion.viaversion.api.minecraft.entitydata.EntityData;\nimport com.viaversion.viaversion.api.minecraft.entitydata.types.EntityDataTypes1_9;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.protocols.v1_9_1to1_9_3.packet.ClientboundPackets1_9_3;\nimport java.util.List;\n\npublic class EntityPacketRewriter1_11 extends LegacyEntityRewriter<ClientboundPackets1_9_3, Protocol1_11To1_10> {\n\n    public EntityPacketRewriter1_11(Protocol1_11To1_10 protocol) {\n        super(protocol);\n    }\n\n    @Override\n    protected void registerPackets() {\n        protocol.registerClientbound(ClientboundPackets1_9_3.LEVEL_EVENT, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.INT);\n                map(Types.BLOCK_POSITION1_8);\n                map(Types.INT);\n                handler(wrapper -> {\n                    int type = wrapper.get(Types.INT, 0);\n                    if (type == 2002 || type == 2007) {\n                        // 2007 potion id doesn't exist in 1.10\n                        if (type == 2007) {\n                            wrapper.set(Types.INT, 0, 2002);\n                        }\n\n                        int mappedData = SplashPotionMappings1_10.getOldData(wrapper.get(Types.INT, 1));\n                        if (mappedData != -1) {\n                            wrapper.set(Types.INT, 1, mappedData);\n                        }\n                    }\n                });\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_9_3.ADD_ENTITY, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT); // 0 - Entity id\n                map(Types.UUID); // 1 - UUID\n                map(Types.BYTE); // 2 - Type\n                map(Types.DOUBLE); // 3 - x\n                map(Types.DOUBLE); // 4 - y\n                map(Types.DOUBLE); // 5 - z\n                map(Types.BYTE); // 6 - Pitch\n                map(Types.BYTE); // 7 - Yaw\n                map(Types.INT); // 8 - data\n\n                // Track Entity\n                handler(getObjectTrackerHandler());\n                handler(getObjectRewriter(EntityTypes1_11.ObjectType::findById));\n\n                handler(protocol.getItemRewriter().getFallingBlockHandler());\n            }\n        });\n\n        registerTracker(ClientboundPackets1_9_3.ADD_EXPERIENCE_ORB, EntityTypes1_11.EntityType.EXPERIENCE_ORB);\n        registerTracker(ClientboundPackets1_9_3.ADD_GLOBAL_ENTITY, EntityTypes1_11.EntityType.LIGHTNING_BOLT);\n\n        protocol.registerClientbound(ClientboundPackets1_9_3.ADD_MOB, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT); // 0 - Entity id\n                map(Types.UUID); // 1 - UUID\n                map(Types.VAR_INT, Types.UNSIGNED_BYTE); // 2 - Entity Type\n                map(Types.DOUBLE); // 3 - X\n                map(Types.DOUBLE); // 4 - Y\n                map(Types.DOUBLE); // 5 - Z\n                map(Types.BYTE); // 6 - Yaw\n                map(Types.BYTE); // 7 - Pitch\n                map(Types.BYTE); // 8 - Head Pitch\n                map(Types.SHORT); // 9 - Velocity X\n                map(Types.SHORT); // 10 - Velocity Y\n                map(Types.SHORT); // 11 - Velocity Z\n                map(Types.ENTITY_DATA_LIST1_9); // 12 - Entity data\n\n                // Track entity\n                handler(getTrackerHandler(Types.UNSIGNED_BYTE, 0));\n\n                // Rewrite entity type / data\n                handler(getMobSpawnRewriter(Types.ENTITY_DATA_LIST1_9));\n\n                // Sub 1.11 clients will error if the list is empty\n                handler(wrapper -> {\n                    List<EntityData> entityDataList = wrapper.get(Types.ENTITY_DATA_LIST1_9, 0);\n                    if (entityDataList.isEmpty()) {\n                        entityDataList.add(new EntityData(0, EntityDataTypes1_9.BYTE, (byte) 0));\n                    }\n                });\n            }\n        });\n\n        registerTracker(ClientboundPackets1_9_3.ADD_PAINTING, EntityTypes1_11.EntityType.PAINTING);\n        registerJoinGame(ClientboundPackets1_9_3.LOGIN, EntityTypes1_11.EntityType.PLAYER);\n        registerRespawn(ClientboundPackets1_9_3.RESPAWN);\n\n        protocol.registerClientbound(ClientboundPackets1_9_3.ADD_PLAYER, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT); // 0 - Entity ID\n                map(Types.UUID); // 1 - Player UUID\n                map(Types.DOUBLE); // 2 - X\n                map(Types.DOUBLE); // 3 - Y\n                map(Types.DOUBLE); // 4 - Z\n                map(Types.BYTE); // 5 - Yaw\n                map(Types.BYTE); // 6 - Pitch\n                map(Types.ENTITY_DATA_LIST1_9); // 7 - Entity data list\n\n                handler(getTrackerAndDataHandler(Types.ENTITY_DATA_LIST1_9, EntityTypes1_11.EntityType.PLAYER));\n                handler(wrapper -> {\n                    // Sub 1.11 clients will cry if the list is empty\n                    List<EntityData> entityDataList = wrapper.get(Types.ENTITY_DATA_LIST1_9, 0);\n                    if (entityDataList.isEmpty()) {\n                        entityDataList.add(new EntityData(0, EntityDataTypes1_9.BYTE, (byte) 0));\n                    }\n                });\n            }\n        });\n\n        registerRemoveEntities(ClientboundPackets1_9_3.REMOVE_ENTITIES);\n        registerSetEntityData(ClientboundPackets1_9_3.SET_ENTITY_DATA, Types.ENTITY_DATA_LIST1_9);\n\n        protocol.registerClientbound(ClientboundPackets1_9_3.ENTITY_EVENT, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.INT); // 0 - Entity ID\n                map(Types.BYTE); // 1 - Entity Status\n\n                handler(wrapper -> {\n                    final int entityId = wrapper.get(Types.INT, 0);\n                    if (entityId != tracker(wrapper.user()).clientEntityId()) {\n                        // Entity events are sent for all players, but we only want to apply this for the self player\n                        return;\n                    }\n\n                    final byte entityStatus = wrapper.get(Types.BYTE, 0);\n                    if (entityStatus == 35) {\n                        // TODO spawn particles?\n                        wrapper.clearPacket();\n                        wrapper.setPacketType(ClientboundPackets1_9_3.GAME_EVENT);\n                        wrapper.write(Types.UNSIGNED_BYTE, (short) 10); // Play Elder Guardian animation\n                        wrapper.write(Types.FLOAT, 0F);\n                    }\n                });\n            }\n        });\n    }\n\n    @Override\n    protected void registerRewrites() {\n        // Guardian\n        mapEntityTypeWithData(EntityTypes1_11.EntityType.ELDER_GUARDIAN, EntityTypes1_11.EntityType.GUARDIAN);\n        // Skeletons\n        mapEntityTypeWithData(EntityTypes1_11.EntityType.WITHER_SKELETON, EntityTypes1_11.EntityType.SKELETON).spawnEntityData(storage -> storage.add(getSkeletonTypeData(1)));\n        mapEntityTypeWithData(EntityTypes1_11.EntityType.STRAY, EntityTypes1_11.EntityType.SKELETON).plainName().spawnEntityData(storage -> storage.add(getSkeletonTypeData(2)));\n        // Zombies\n        mapEntityTypeWithData(EntityTypes1_11.EntityType.HUSK, EntityTypes1_11.EntityType.ZOMBIE).plainName().spawnEntityData(storage -> handleZombieType(storage, 6));\n        mapEntityTypeWithData(EntityTypes1_11.EntityType.ZOMBIE_VILLAGER, EntityTypes1_11.EntityType.ZOMBIE).spawnEntityData(storage -> handleZombieType(storage, 1));\n        // Horses\n        mapEntityTypeWithData(EntityTypes1_11.EntityType.HORSE, EntityTypes1_11.EntityType.HORSE).spawnEntityData(storage -> storage.add(getHorseDataType(0))); // Nob able to ride the horse without having the EntityDataType sent.\n        mapEntityTypeWithData(EntityTypes1_11.EntityType.DONKEY, EntityTypes1_11.EntityType.HORSE).spawnEntityData(storage -> storage.add(getHorseDataType(1)));\n        mapEntityTypeWithData(EntityTypes1_11.EntityType.MULE, EntityTypes1_11.EntityType.HORSE).spawnEntityData(storage -> storage.add(getHorseDataType(2)));\n        mapEntityTypeWithData(EntityTypes1_11.EntityType.SKELETON_HORSE, EntityTypes1_11.EntityType.HORSE).spawnEntityData(storage -> storage.add(getHorseDataType(4)));\n        mapEntityTypeWithData(EntityTypes1_11.EntityType.ZOMBIE_HORSE, EntityTypes1_11.EntityType.HORSE).spawnEntityData(storage -> storage.add(getHorseDataType(3)));\n        // New mobs\n        mapEntityTypeWithData(EntityTypes1_11.EntityType.EVOKER_FANGS, EntityTypes1_11.EntityType.SHULKER);\n        mapEntityTypeWithData(EntityTypes1_11.EntityType.EVOKER, EntityTypes1_11.EntityType.VILLAGER).plainName();\n        mapEntityTypeWithData(EntityTypes1_11.EntityType.VEX, EntityTypes1_11.EntityType.BAT).plainName();\n        mapEntityTypeWithData(EntityTypes1_11.EntityType.VINDICATOR, EntityTypes1_11.EntityType.VILLAGER).plainName().spawnEntityData(storage -> storage.add(new EntityData(13, EntityDataTypes1_9.VAR_INT, 4))); // Base Profession\n        mapEntityTypeWithData(EntityTypes1_11.EntityType.LLAMA, EntityTypes1_11.EntityType.HORSE).plainName().spawnEntityData(storage -> storage.add(getHorseDataType(1)));\n        mapEntityTypeWithData(EntityTypes1_11.EntityType.LLAMA_SPIT, EntityTypes1_11.EntityType.SNOWBALL);\n\n        mapObjectType(EntityTypes1_11.ObjectType.LLAMA_SPIT, EntityTypes1_11.ObjectType.SNOWBALL, -1);\n        // Replace with endertorchthingies\n        mapObjectType(EntityTypes1_11.ObjectType.EVOKER_FANGS, EntityTypes1_11.ObjectType.FALLING_BLOCK, 198 | 1 << 12);\n\n        // Handle ElderGuardian & target entity data\n        filter().type(EntityTypes1_11.EntityType.GUARDIAN).index(12).handler((event, data) -> {\n            boolean b = (boolean) data.getValue();\n            int bitmask = b ? 0x02 : 0;\n\n            if (event.entityType() == EntityTypes1_11.EntityType.ELDER_GUARDIAN) {\n                bitmask |= 0x04;\n            }\n\n            data.setTypeAndValue(EntityDataTypes1_9.BYTE, (byte) bitmask);\n        });\n\n        // Handle skeleton swing\n        filter().type(EntityTypes1_11.EntityType.ABSTRACT_SKELETON).index(12).toIndex(13);\n\n        /*\n            ZOMBIE CHANGES\n         */\n        filter().type(EntityTypes1_11.EntityType.ZOMBIE).handler((event, data) -> {\n            switch (data.id()) {\n                case 13 -> event.cancel();\n                case 14 -> event.setIndex(15);\n                case 15 -> event.setIndex(14);\n                case 16 -> {\n                    // Profession\n                    event.setIndex(13);\n                    data.setValue(1 + (int) data.getValue());\n                }\n            }\n        });\n\n        // Handle Evocation Illager\n        filter().type(EntityTypes1_11.EntityType.EVOKER).index(12).handler((event, data) -> {\n            event.setIndex(13);\n            data.setTypeAndValue(EntityDataTypes1_9.VAR_INT, ((Byte) data.getValue()).intValue()); // Change the profession for the states\n        });\n\n        // Handle Vex (Remove this field completely since the position is not updated correctly when idling for bats. Sad ):\n        filter().type(EntityTypes1_11.EntityType.VEX).index(12).handler((event, data) -> {\n            data.setValue((byte) 0x00);\n        });\n\n        // Handle VindicationIllager\n        filter().type(EntityTypes1_11.EntityType.VINDICATOR).index(12).handler((event, data) -> {\n            event.setIndex(13);\n            data.setTypeAndValue(EntityDataTypes1_9.VAR_INT, ((Number) data.getValue()).intValue() == 1 ? 2 : 4);\n        });\n\n        /*\n            HORSES\n         */\n\n        // Handle horse flags\n        filter().type(EntityTypes1_11.EntityType.ABSTRACT_HORSE).index(13).handler((event, data) -> {\n            StoredEntityData entityData = storedEntityData(event);\n            byte b = (byte) data.getValue();\n            if (entityData.has(ChestedHorseStorage.class) && entityData.get(ChestedHorseStorage.class).isChested()) {\n                b |= 0x08; // Chested\n                data.setValue(b);\n            }\n        });\n\n        // Create chested horse storage\n        filter().type(EntityTypes1_11.EntityType.CHESTED_HORSE).handler((event, data) -> {\n            StoredEntityData entityData = storedEntityData(event);\n            if (!entityData.has(ChestedHorseStorage.class)) {\n                entityData.put(new ChestedHorseStorage());\n            }\n        });\n\n        // Handle horse armor\n        filter().type(EntityTypes1_11.EntityType.HORSE).index(16).toIndex(17);\n\n        // Handle chested horse\n        filter().type(EntityTypes1_11.EntityType.CHESTED_HORSE).index(15).handler((event, data) -> {\n            StoredEntityData entityData = storedEntityData(event);\n            ChestedHorseStorage storage = entityData.get(ChestedHorseStorage.class);\n            boolean b = (boolean) data.getValue();\n            storage.setChested(b);\n            event.cancel();\n        });\n\n        // Get rid of Liama entity data\n        filter().type(EntityTypes1_11.EntityType.LLAMA).handler((event, data) -> {\n            StoredEntityData entityData = storedEntityData(event);\n            ChestedHorseStorage storage = entityData.get(ChestedHorseStorage.class);\n\n            int index = event.index();\n            // Store them for later (:\n            switch (index) {\n                case 16 -> {\n                    storage.setLiamaStrength((int) data.getValue());\n                    event.cancel();\n                }\n                case 17 -> {\n                    storage.setLiamaCarpetColor((int) data.getValue());\n                    event.cancel();\n                }\n                case 18 -> {\n                    storage.setLiamaVariant((int) data.getValue());\n                    event.cancel();\n                }\n            }\n        });\n\n        // Handle Horse (Correct owner)\n        filter().type(EntityTypes1_11.EntityType.ABSTRACT_HORSE).index(14).toIndex(16);\n\n        // Handle villager - Change non-existing profession\n        filter().type(EntityTypes1_11.EntityType.VILLAGER).index(13).handler((event, data) -> {\n            if ((int) data.getValue() == 5) {\n                data.setValue(0);\n            }\n        });\n\n        // handle new Shulker color data\n        filter().type(EntityTypes1_11.EntityType.SHULKER).cancel(15);\n    }\n\n    /*\n        0 - Skeleton\n        1 - Wither Skeleton\n        2 - Stray\n     */\n\n    private EntityData getSkeletonTypeData(int type) {\n        return new EntityData(12, EntityDataTypes1_9.VAR_INT, type);\n    }\n\n    /*\n        0 - Zombie\n        1-5 - Villager with profession\n        6 - Husk\n     */\n    private EntityData getZombieTypeData(int type) {\n        return new EntityData(13, EntityDataTypes1_9.VAR_INT, type);\n    }\n\n    private void handleZombieType(WrappedEntityData storage, int type) {\n        EntityData meta = storage.get(13);\n        if (meta == null) {\n            storage.add(getZombieTypeData(type));\n        }\n    }\n\n    /*\n        Horse 0\n        Donkey 1\n        Mule 2\n        Zombie horse 3\n        Skeleton horse 4\n    */\n    private EntityData getHorseDataType(int type) {\n        return new EntityData(14, EntityDataTypes1_9.VAR_INT, type);\n    }\n\n    @Override\n    public EntityType typeFromId(int typeId) {\n        return EntityTypes1_11.EntityType.findById(typeId);\n    }\n\n    @Override\n    public EntityType objectTypeFromId(int typeId, int data) {\n        return EntityTypes1_11.ObjectType.getEntityType(typeId, data);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_11to1_10/rewriter/PlayerPacketRewriter1_11.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.viaversion.viabackwards.protocol.v1_11to1_10.rewriter;\n\nimport com.viaversion.viabackwards.protocol.v1_11to1_10.Protocol1_11To1_10;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.protocol.remapper.ValueTransformer;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.libs.gson.JsonElement;\nimport com.viaversion.viaversion.libs.gson.JsonObject;\nimport com.viaversion.viaversion.protocols.v1_9_1to1_9_3.packet.ClientboundPackets1_9_3;\nimport com.viaversion.viaversion.protocols.v1_9_1to1_9_3.packet.ServerboundPackets1_9_3;\nimport com.viaversion.viaversion.util.ComponentUtil;\n\npublic class PlayerPacketRewriter1_11 {\n    private static final ValueTransformer<Short, Float> TO_NEW_FLOAT = new ValueTransformer<>(Types.FLOAT) {\n        @Override\n        public Float transform(PacketWrapper wrapper, Short inputValue) {\n            return inputValue / 16f;\n        }\n    };\n\n    public static void register(Protocol1_11To1_10 protocol) {\n        protocol.registerClientbound(ClientboundPackets1_9_3.SET_TITLES, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT); // 0 - Action\n\n                handler(wrapper -> {\n                    int action = wrapper.get(Types.VAR_INT, 0);\n\n                    if (action == 2) { // Handle the new ActionBar\n                        JsonElement message = wrapper.read(Types.COMPONENT);\n\n                        wrapper.clearPacket();\n                        wrapper.setPacketType(ClientboundPackets1_9_3.CHAT);\n\n                        // https://bugs.mojang.com/browse/MC-119145\n                        String legacy = ComponentUtil.jsonToLegacy(message);\n                        message = new JsonObject();\n                        message.getAsJsonObject().addProperty(\"text\", legacy);\n\n                        wrapper.write(Types.COMPONENT, message);\n                        wrapper.write(Types.BYTE, (byte) 2);\n                    } else if (action > 2) {\n                        wrapper.set(Types.VAR_INT, 0, action - 1); // Move everything one position down\n                    }\n                });\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_9_3.TAKE_ITEM_ENTITY, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT); // 0 - Collected entity id\n                map(Types.VAR_INT); // 1 - Collector entity id\n\n                handler(wrapper -> wrapper.read(Types.VAR_INT)); // Ignore item pickup count\n            }\n        });\n\n\n        protocol.registerServerbound(ServerboundPackets1_9_3.USE_ITEM_ON, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.BLOCK_POSITION1_8); // 0 - Location\n                map(Types.VAR_INT); // 1 - Face\n                map(Types.VAR_INT); // 2 - Hand\n\n                map(Types.UNSIGNED_BYTE, TO_NEW_FLOAT);\n                map(Types.UNSIGNED_BYTE, TO_NEW_FLOAT);\n                map(Types.UNSIGNED_BYTE, TO_NEW_FLOAT);\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_11to1_10/storage/ChestedHorseStorage.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.viaversion.viabackwards.protocol.v1_11to1_10.storage;\n\npublic class ChestedHorseStorage {\n    private boolean chested;\n    private int liamaStrength;\n    private int liamaCarpetColor = -1;\n    private int liamaVariant;\n\n    public boolean isChested() {\n        return chested;\n    }\n\n    public void setChested(boolean chested) {\n        this.chested = chested;\n    }\n\n    public int getLiamaStrength() {\n        return liamaStrength;\n    }\n\n    public void setLiamaStrength(int liamaStrength) {\n        this.liamaStrength = liamaStrength;\n    }\n\n    public int getLiamaCarpetColor() {\n        return liamaCarpetColor;\n    }\n\n    public void setLiamaCarpetColor(int liamaCarpetColor) {\n        this.liamaCarpetColor = liamaCarpetColor;\n    }\n\n    public int getLiamaVariant() {\n        return liamaVariant;\n    }\n\n    public void setLiamaVariant(int liamaVariant) {\n        this.liamaVariant = liamaVariant;\n    }\n\n    @Override\n    public String toString() {\n        return \"ChestedHorseStorage{\" + \"chested=\" + chested + \", liamaStrength=\" + liamaStrength + \", liamaCarpetColor=\" + liamaCarpetColor + \", liamaVariant=\" + liamaVariant + '}';\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_11to1_10/storage/WindowTracker.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.viaversion.viabackwards.protocol.v1_11to1_10.storage;\n\nimport com.viaversion.viaversion.api.connection.StorableObject;\n\npublic class WindowTracker implements StorableObject {\n    private String inventory;\n    private int entityId = -1;\n\n    public String getInventory() {\n        return inventory;\n    }\n\n    public void setInventory(String inventory) {\n        this.inventory = inventory;\n    }\n\n    public int getEntityId() {\n        return entityId;\n    }\n\n    public void setEntityId(int entityId) {\n        this.entityId = entityId;\n    }\n\n    @Override\n    public String toString() {\n        return \"WindowTracker{\" + \"inventory='\" + inventory + '\\'' + \", entityId=\" + entityId + '}';\n    }\n}\n\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_12_1to1_12/Protocol1_12_1To1_12.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_12_1to1_12;\n\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viaversion.protocols.v1_11_1to1_12.packet.ClientboundPackets1_12;\nimport com.viaversion.viaversion.protocols.v1_11_1to1_12.packet.ServerboundPackets1_12;\nimport com.viaversion.viaversion.protocols.v1_12to1_12_1.packet.ClientboundPackets1_12_1;\nimport com.viaversion.viaversion.protocols.v1_12to1_12_1.packet.ServerboundPackets1_12_1;\n\npublic class Protocol1_12_1To1_12 extends BackwardsProtocol<ClientboundPackets1_12_1, ClientboundPackets1_12, ServerboundPackets1_12_1, ServerboundPackets1_12> {\n\n    public Protocol1_12_1To1_12() {\n        super(ClientboundPackets1_12_1.class, ClientboundPackets1_12.class, ServerboundPackets1_12_1.class, ServerboundPackets1_12.class);\n    }\n\n    @Override\n    protected void registerPackets() {\n        cancelClientbound(ClientboundPackets1_12_1.PLACE_GHOST_RECIPE);\n        cancelServerbound(ServerboundPackets1_12.CRAFTING_RECIPE_PLACEMENT);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_12_2to1_12_1/Protocol1_12_2To1_12_1.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.viaversion.viabackwards.protocol.v1_12_2to1_12_1;\n\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viabackwards.protocol.v1_12_2to1_12_1.storage.KeepAliveTracker;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.protocols.v1_12to1_12_1.packet.ClientboundPackets1_12_1;\nimport com.viaversion.viaversion.protocols.v1_12to1_12_1.packet.ServerboundPackets1_12_1;\n\npublic class Protocol1_12_2To1_12_1 extends BackwardsProtocol<ClientboundPackets1_12_1, ClientboundPackets1_12_1, ServerboundPackets1_12_1, ServerboundPackets1_12_1> {\n\n    public Protocol1_12_2To1_12_1() {\n        super(ClientboundPackets1_12_1.class, ClientboundPackets1_12_1.class, ServerboundPackets1_12_1.class, ServerboundPackets1_12_1.class);\n    }\n\n    @Override\n    protected void registerPackets() {\n        registerClientbound(ClientboundPackets1_12_1.KEEP_ALIVE, new PacketHandlers() {\n            @Override\n            public void register() {\n                handler(packetWrapper -> {\n                    Long keepAlive = packetWrapper.read(Types.LONG);\n                    packetWrapper.user().get(KeepAliveTracker.class).setKeepAlive(keepAlive);\n                    packetWrapper.write(Types.VAR_INT, keepAlive.hashCode());\n                });\n            }\n        });\n\n        registerServerbound(ServerboundPackets1_12_1.KEEP_ALIVE, new PacketHandlers() {\n            @Override\n            public void register() {\n                handler(packetWrapper -> {\n                    int keepAlive = packetWrapper.read(Types.VAR_INT);\n                    long realKeepAlive = packetWrapper.user().get(KeepAliveTracker.class).getKeepAlive();\n                    if (keepAlive != Long.hashCode(realKeepAlive)) {\n                        packetWrapper.cancel(); // Wrong data, cancel packet\n                        return;\n                    }\n                    packetWrapper.write(Types.LONG, realKeepAlive);\n                    // Reset KeepAliveTracker (to prevent sending same valid value in a row causing a timeout)\n                    packetWrapper.user().get(KeepAliveTracker.class).setKeepAlive(Integer.MAX_VALUE);\n                });\n            }\n        });\n    }\n\n    @Override\n    public void init(UserConnection userConnection) {\n        userConnection.put(new KeepAliveTracker());\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_12_2to1_12_1/storage/KeepAliveTracker.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.viaversion.viabackwards.protocol.v1_12_2to1_12_1.storage;\n\nimport com.viaversion.viaversion.api.connection.StorableObject;\n\npublic class KeepAliveTracker implements StorableObject {\n    private long keepAlive = Integer.MAX_VALUE;\n\n    public long getKeepAlive() {\n        return keepAlive;\n    }\n\n    public void setKeepAlive(long keepAlive) {\n        this.keepAlive = keepAlive;\n    }\n\n    @Override\n    public String toString() {\n        return \"KeepAliveTracker{\" + \"keepAlive=\" + keepAlive + '}';\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_12to1_11_1/Protocol1_12To1_11_1.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.viaversion.viabackwards.protocol.v1_12to1_11_1;\n\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viabackwards.api.data.BackwardsMappingData;\nimport com.viaversion.viabackwards.protocol.v1_12to1_11_1.rewriter.BlockItemPacketRewriter1_12;\nimport com.viaversion.viabackwards.protocol.v1_12to1_11_1.rewriter.ComponentRewriter1_12;\nimport com.viaversion.viabackwards.protocol.v1_12to1_11_1.rewriter.EntityPacketRewriter1_12;\nimport com.viaversion.viabackwards.protocol.v1_12to1_11_1.rewriter.SoundPacketRewriter1_12;\nimport com.viaversion.viabackwards.protocol.v1_12to1_11_1.storage.ShoulderTracker;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.ClientWorld;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_12;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.data.entity.EntityTrackerBase;\nimport com.viaversion.viaversion.protocols.v1_11_1to1_12.packet.ClientboundPackets1_12;\nimport com.viaversion.viaversion.protocols.v1_11_1to1_12.packet.ServerboundPackets1_12;\nimport com.viaversion.viaversion.protocols.v1_9_1to1_9_3.packet.ClientboundPackets1_9_3;\nimport com.viaversion.viaversion.protocols.v1_9_1to1_9_3.packet.ServerboundPackets1_9_3;\nimport com.viaversion.viaversion.util.ComponentUtil;\nimport com.viaversion.viaversion.util.SerializerVersion;\n\npublic class Protocol1_12To1_11_1 extends BackwardsProtocol<ClientboundPackets1_12, ClientboundPackets1_9_3, ServerboundPackets1_12, ServerboundPackets1_9_3> {\n\n    private static final BackwardsMappingData MAPPINGS = new BackwardsMappingData(\"1.12\", \"1.11\");\n    private final EntityPacketRewriter1_12 entityRewriter = new EntityPacketRewriter1_12(this);\n    private final BlockItemPacketRewriter1_12 itemRewriter = new BlockItemPacketRewriter1_12(this);\n    private final ComponentRewriter1_12 componentRewriter = new ComponentRewriter1_12(this);\n\n    public Protocol1_12To1_11_1() {\n        super(ClientboundPackets1_12.class, ClientboundPackets1_9_3.class, ServerboundPackets1_12.class, ServerboundPackets1_9_3.class);\n    }\n\n    @Override\n    protected void registerPackets() {\n        super.registerPackets();\n\n        componentRewriter.registerComponentPacket(ClientboundPackets1_12.CHAT);\n        new SoundPacketRewriter1_12(this).register();\n\n        registerClientbound(ClientboundPackets1_12.SET_TITLES, wrapper -> {\n            int action = wrapper.passthrough(Types.VAR_INT);\n            if (action >= 0 && action <= 2) {\n                // Should be done globally in the component rewriter, but /shrug for now\n                String component = wrapper.read(Types.COMPONENT).toString();\n                wrapper.write(Types.COMPONENT, ComponentUtil.convertJsonOrEmpty(component, SerializerVersion.V1_12, SerializerVersion.V1_9));\n            }\n        });\n\n        cancelClientbound(ClientboundPackets1_12.UPDATE_ADVANCEMENTS);\n        cancelClientbound(ClientboundPackets1_12.RECIPE);\n        cancelClientbound(ClientboundPackets1_12.SELECT_ADVANCEMENTS_TAB);\n    }\n\n    @Override\n    public void init(UserConnection user) {\n        user.addEntityTracker(this.getClass(), new EntityTrackerBase(user, EntityTypes1_12.EntityType.PLAYER));\n        user.addClientWorld(this.getClass(), new ClientWorld());\n\n        user.put(new ShoulderTracker(user));\n    }\n\n    @Override\n    public BackwardsMappingData getMappingData() {\n        return MAPPINGS;\n    }\n\n    @Override\n    public EntityPacketRewriter1_12 getEntityRewriter() {\n        return entityRewriter;\n    }\n\n    @Override\n    public BlockItemPacketRewriter1_12 getItemRewriter() {\n        return itemRewriter;\n    }\n\n    @Override\n    public ComponentRewriter1_12 getComponentRewriter() {\n        return componentRewriter;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_12to1_11_1/data/BlockColors1_11_1.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.viaversion.viabackwards.protocol.v1_12to1_11_1.data;\n\npublic class BlockColors1_11_1 {\n    private static final String[] COLORS = new String[16];\n\n    static {\n        COLORS[0] = \"White\";\n        COLORS[1] = \"Orange\";\n        COLORS[2] = \"Magenta\";\n        COLORS[3] = \"Light Blue\";\n        COLORS[4] = \"Yellow\";\n        COLORS[5] = \"Lime\";\n        COLORS[6] = \"Pink\";\n        COLORS[7] = \"Gray\";\n        COLORS[8] = \"Light Gray\";\n        COLORS[9] = \"Cyan\";\n        COLORS[10] = \"Purple\";\n        COLORS[11] = \"Blue\";\n        COLORS[12] = \"Brown\";\n        COLORS[13] = \"Green\";\n        COLORS[14] = \"Red\";\n        COLORS[15] = \"Black\";\n    }\n\n    public static String get(int key) {\n        return key >= 0 && key < COLORS.length ? COLORS[key] : \"Unknown color\";\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_12to1_11_1/data/MapColorMappings1_11_1.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_12to1_11_1.data;\n\nimport com.viaversion.viaversion.libs.fastutil.ints.Int2IntMap;\nimport com.viaversion.viaversion.libs.fastutil.ints.Int2IntOpenHashMap;\n\npublic class MapColorMappings1_11_1 {\n    private static final Int2IntMap MAPPING = new Int2IntOpenHashMap(64, 0.99F);\n\n    static {\n        MAPPING.defaultReturnValue(-1);\n        MAPPING.put(144, 59); // (148, 124, 114) -> (148, 124, 114)\n        MAPPING.put(145, 56); // (180, 153, 139) -> (180, 153, 139)\n        MAPPING.put(146, 56); // (209, 177, 161) -> (209, 177, 161)\n        MAPPING.put(147, 45); // (111, 94, 85) -> (111, 94, 85)\n        MAPPING.put(148, 63); // (112, 58, 25) -> (112, 58, 25)\n        MAPPING.put(149, 60); // (137, 71, 31) -> (137, 71, 31)\n        MAPPING.put(150, 60); // (159, 82, 36) -> (159, 82, 36)\n        MAPPING.put(151, 136); // (84, 43, 19) -> (84, 43, 19)\n        MAPPING.put(152, 83); // (105, 61, 76) -> (105, 61, 76)\n        MAPPING.put(153, 83); // (129, 75, 93) -> (129, 75, 93)\n        MAPPING.put(154, 80); // (149, 87, 108) -> (149, 87, 108)\n        MAPPING.put(155, 115); // (79, 46, 57) -> (79, 46, 57)\n        MAPPING.put(156, 39); // (79, 76, 97) -> (79, 76, 97)\n        MAPPING.put(157, 39); // (97, 93, 119) -> (97, 93, 119)\n        MAPPING.put(158, 36); // (112, 108, 138) -> (112, 108, 138)\n        MAPPING.put(159, 47); // (59, 57, 73) -> (59, 57, 73)\n        MAPPING.put(160, 60); // (131, 94, 25) -> (131, 94, 25)\n        MAPPING.put(161, 61); // (160, 115, 31) -> (160, 115, 31)\n        MAPPING.put(162, 62); // (186, 133, 36) -> (186, 133, 36)\n        MAPPING.put(163, 137); // (98, 70, 19) -> (98, 70, 19)\n        MAPPING.put(164, 108); // (73, 83, 37) -> (73, 83, 37)\n        MAPPING.put(165, 108); // (89, 101, 46) -> (89, 101, 46)\n        MAPPING.put(166, 109); // (103, 117, 53) -> (103, 117, 53)\n        MAPPING.put(167, 111); // (55, 62, 28) -> (55, 62, 28)\n        MAPPING.put(168, 112); // (113, 54, 55) -> (113, 54, 55)\n        MAPPING.put(169, 113); // (138, 66, 67) -> (138, 66, 67)\n        MAPPING.put(170, 114); // (160, 77, 78) -> (160, 77, 78)\n        MAPPING.put(171, 115); // (85, 41, 41) -> (85, 41, 41)\n        MAPPING.put(172, 118); // (40, 29, 25) -> (40, 29, 25)\n        MAPPING.put(173, 107); // (49, 35, 30) -> (49, 35, 30)\n        MAPPING.put(174, 107); // (57, 41, 35) -> (57, 41, 35)\n        MAPPING.put(175, 118); // (30, 22, 19) -> (30, 22, 19)\n        MAPPING.put(176, 91); // (95, 76, 69) -> (95, 76, 69)\n        MAPPING.put(177, 45); // (116, 92, 85) -> (116, 92, 85)\n        MAPPING.put(178, 46); // (135, 107, 98) -> (135, 107, 98)\n        MAPPING.put(179, 47); // (71, 57, 52) -> (71, 57, 52)\n        MAPPING.put(180, 85); // (61, 65, 65) -> (61, 65, 65)\n        MAPPING.put(181, 44); // (75, 79, 79) -> (75, 79, 79)\n        MAPPING.put(182, 27); // (87, 92, 92) -> (87, 92, 92)\n        MAPPING.put(183, 84); // (46, 49, 49) -> (46, 49, 49)\n        MAPPING.put(184, 83); // (86, 52, 62) -> (86, 52, 62)\n        MAPPING.put(185, 83); // (105, 63, 76) -> (105, 63, 76)\n        MAPPING.put(186, 83); // (122, 73, 88) -> (122, 73, 88)\n        MAPPING.put(187, 84); // (65, 39, 47) -> (65, 39, 47)\n        MAPPING.put(188, 84); // (54, 44, 65) -> (54, 44, 65)\n        MAPPING.put(189, 71); // (66, 53, 79) -> (66, 53, 79)\n        MAPPING.put(190, 71); // (76, 62, 92) -> (76, 62, 92)\n        MAPPING.put(191, 87); // (40, 33, 49) -> (40, 33, 49)\n        MAPPING.put(192, 107); // (54, 35, 25) -> (54, 35, 25)\n        MAPPING.put(193, 139); // (66, 43, 30) -> (66, 43, 30)\n        MAPPING.put(194, 43); // (76, 50, 35) -> (76, 50, 35)\n        MAPPING.put(195, 107); // (40, 26, 19) -> (40, 26, 19)\n        MAPPING.put(196, 111); // (54, 58, 30) -> (54, 58, 30)\n        MAPPING.put(197, 111); // (66, 71, 36) -> (66, 71, 36)\n        MAPPING.put(198, 111); // (76, 82, 42) -> (76, 82, 42)\n        MAPPING.put(199, 107); // (40, 43, 22) -> (40, 43, 22)\n        MAPPING.put(200, 112); // (100, 42, 32) -> (100, 42, 32)\n        MAPPING.put(201, 113); // (123, 52, 40) -> (123, 52, 40)\n        MAPPING.put(202, 113); // (142, 60, 46) -> (142, 60, 46)\n        MAPPING.put(203, 115); // (75, 32, 24) -> (75, 32, 24)\n        MAPPING.put(204, 116); // (26, 16, 11) -> (26, 16, 11)\n        MAPPING.put(205, 117); // (32, 19, 14) -> (32, 19, 14)\n        MAPPING.put(206, 107); // (37, 22, 16) -> (37, 22, 16)\n        MAPPING.put(207, 119); // (20, 12, 8) -> (20, 12, 8)\n    }\n\n    public static int getNearestOldColor(int color) {\n        return MAPPING.getOrDefault(color, color);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_12to1_11_1/rewriter/BlockItemPacketRewriter1_12.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.viaversion.viabackwards.protocol.v1_12to1_11_1.rewriter;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.IntArrayTag;\nimport com.viaversion.nbt.tag.LongArrayTag;\nimport com.viaversion.nbt.tag.StringTag;\nimport com.viaversion.nbt.tag.Tag;\nimport com.viaversion.viabackwards.api.rewriters.LegacyBlockItemRewriter;\nimport com.viaversion.viabackwards.protocol.v1_12to1_11_1.Protocol1_12To1_11_1;\nimport com.viaversion.viabackwards.protocol.v1_12to1_11_1.data.MapColorMappings1_11_1;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.ClientWorld;\nimport com.viaversion.viaversion.api.minecraft.chunks.Chunk;\nimport com.viaversion.viaversion.api.minecraft.item.Item;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.chunk.ChunkType1_9_3;\nimport com.viaversion.viaversion.protocols.v1_11_1to1_12.packet.ClientboundPackets1_12;\nimport com.viaversion.viaversion.protocols.v1_11_1to1_12.packet.ServerboundPackets1_12;\nimport com.viaversion.viaversion.protocols.v1_9_1to1_9_3.packet.ServerboundPackets1_9_3;\nimport com.viaversion.viaversion.util.ComponentUtil;\nimport com.viaversion.viaversion.util.Key;\nimport com.viaversion.viaversion.util.SerializerVersion;\nimport java.util.Iterator;\nimport java.util.Map;\nimport org.checkerframework.checker.nullness.qual.Nullable;\n\npublic class BlockItemPacketRewriter1_12 extends LegacyBlockItemRewriter<ClientboundPackets1_12, ServerboundPackets1_9_3, Protocol1_12To1_11_1> {\n\n    public BlockItemPacketRewriter1_12(Protocol1_12To1_11_1 protocol) {\n        super(protocol, \"1.12\");\n    }\n\n    @Override\n    protected void registerPackets() {\n        registerBlockChange(ClientboundPackets1_12.BLOCK_UPDATE);\n        registerMultiBlockChange(ClientboundPackets1_12.CHUNK_BLOCKS_UPDATE);\n\n        protocol.registerClientbound(ClientboundPackets1_12.MAP_ITEM_DATA, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT);\n                map(Types.BYTE);\n                map(Types.BOOLEAN);\n                handler(wrapper -> {\n                    int count = wrapper.passthrough(Types.VAR_INT);\n                    for (int i = 0; i < count * 3; i++) {\n                        wrapper.passthrough(Types.BYTE);\n                    }\n                });\n                handler(wrapper -> {\n                    short columns = wrapper.passthrough(Types.UNSIGNED_BYTE);\n                    if (columns <= 0) return;\n\n                    wrapper.passthrough(Types.UNSIGNED_BYTE); // Rows\n                    wrapper.passthrough(Types.UNSIGNED_BYTE); // X\n                    wrapper.passthrough(Types.UNSIGNED_BYTE); // Z\n                    byte[] data = wrapper.read(Types.BYTE_ARRAY_PRIMITIVE);\n                    for (int i = 0; i < data.length; i++) {\n                        short color = (short) (data[i] & 0xFF);\n                        if (color > 143) {\n                            color = (short) MapColorMappings1_11_1.getNearestOldColor(color);\n                            data[i] = (byte) color;\n                        }\n                    }\n                    wrapper.write(Types.BYTE_ARRAY_PRIMITIVE, data);\n                });\n            }\n        });\n\n        registerSetSlot(ClientboundPackets1_12.CONTAINER_SET_SLOT);\n        registerSetContent(ClientboundPackets1_12.CONTAINER_SET_CONTENT);\n        registerSetEquippedItem(ClientboundPackets1_12.SET_EQUIPPED_ITEM);\n        registerCustomPayloadTradeList(ClientboundPackets1_12.CUSTOM_PAYLOAD);\n\n        protocol.registerServerbound(ServerboundPackets1_9_3.CONTAINER_CLICK, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.BYTE); // 0 - Window ID\n                map(Types.SHORT); // 1 - Slot\n                map(Types.BYTE); // 2 - Button\n                map(Types.SHORT); // 3 - Action number\n                map(Types.VAR_INT); // 4 - Mode\n                map(Types.ITEM1_8); // 5 - Clicked Item\n\n                handler(wrapper -> {\n                    if (wrapper.get(Types.VAR_INT, 0) == 1) { // Shift click\n                        // https://github.com/ViaVersion/ViaVersion/pull/754\n                        // Previously clients grab the item from the clicked slot *before* it has\n                        // been moved however now they grab the slot item *after* it has been moved\n                        // and send that in the packet.\n                        wrapper.set(Types.ITEM1_8, 0, null); // Set null item (probably will work)\n\n                        // Apologize (may happen in some cases, maybe if inventory is full?)\n                        PacketWrapper confirm = wrapper.create(ServerboundPackets1_12.CONTAINER_ACK);\n                        confirm.write(Types.BYTE, wrapper.get(Types.BYTE, 0));\n                        confirm.write(Types.SHORT, wrapper.get(Types.SHORT, 1));\n                        confirm.write(Types.BOOLEAN, true); // Success - not used\n\n                        wrapper.sendToServer(Protocol1_12To1_11_1.class);\n                        wrapper.cancel();\n                        confirm.sendToServer(Protocol1_12To1_11_1.class);\n                        return;\n\n                    }\n                    Item item = wrapper.get(Types.ITEM1_8, 0);\n                    handleItemToServer(wrapper.user(), item);\n                });\n            }\n        });\n\n        registerSetCreativeModeSlot(ServerboundPackets1_9_3.SET_CREATIVE_MODE_SLOT);\n\n        protocol.registerClientbound(ClientboundPackets1_12.LEVEL_CHUNK, wrapper -> {\n            ClientWorld clientWorld = wrapper.user().getClientWorld(Protocol1_12To1_11_1.class);\n\n            ChunkType1_9_3 type = ChunkType1_9_3.forEnvironment(clientWorld.getEnvironment()); // Use the 1.9.4 Chunk type since nothing changed.\n            Chunk chunk = wrapper.passthrough(type);\n\n            handleChunk(chunk);\n            for (final CompoundTag tag : chunk.getBlockEntities()) {\n                final String id = tag.getString(\"id\");\n                if (id == null) {\n                    continue;\n                }\n                if (Key.stripMinecraftNamespace(id).equals(\"sign\")) {\n                    handleSignText(tag);\n                }\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_12.BLOCK_ENTITY_DATA, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.BLOCK_POSITION1_8); // 0 - Position\n                map(Types.UNSIGNED_BYTE); // 1 - Action\n                map(Types.NAMED_COMPOUND_TAG); // 2 - NBT\n\n                handler(wrapper -> {\n                    final short type = wrapper.get(Types.UNSIGNED_BYTE, 0);\n                    if (type == 9) {\n                        final CompoundTag tag = wrapper.get(Types.NAMED_COMPOUND_TAG, 0);\n                        handleSignText(tag);\n                    } else if (type == 11) {\n                        // Remove bed color\n                        wrapper.cancel();\n                    }\n                });\n            }\n        });\n\n        protocol.getEntityRewriter().filter().handler((event, data) -> {\n            if (data.dataType().type().equals(Types.ITEM1_8)) // Is Item\n                data.setValue(handleItemToClient(event.user(), (Item) data.getValue()));\n        });\n\n        protocol.registerServerbound(ServerboundPackets1_9_3.CLIENT_COMMAND, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT); // Action ID\n\n                handler(wrapper -> {\n                    // Open Inventory\n                    if (wrapper.get(Types.VAR_INT, 0) == 2) {\n                        wrapper.cancel();\n                    }\n                });\n            }\n        });\n    }\n\n    private void handleSignText(final CompoundTag tag) {\n        // Push signs through component conversion, fixes https://github.com/ViaVersion/ViaBackwards/issues/835\n        for (int i = 0; i < 4; i++) {\n            final StringTag lineTag = tag.getStringTag(\"Text\" + (i + 1));\n            if (lineTag == null) {\n                continue;\n            }\n\n            lineTag.setValue(ComponentUtil.convertJsonOrEmpty(lineTag.getValue(), SerializerVersion.V1_12, SerializerVersion.V1_9).toString());\n        }\n    }\n\n    @Override\n    public @Nullable Item handleItemToClient(UserConnection connection, Item item) {\n        if (item == null) return null;\n        super.handleItemToClient(connection, item);\n\n        if (item.tag() != null) {\n            CompoundTag backupTag = new CompoundTag();\n            if (handleNbtToClient(item.tag(), backupTag)) {\n                item.tag().put(\"Via|LongArrayTags\", backupTag);\n            }\n        }\n\n        return item;\n    }\n\n    private boolean handleNbtToClient(CompoundTag compoundTag, CompoundTag backupTag) {\n        // Long array tags were introduced in 1.12 - just remove them\n        // Only save the removed tags instead of blindly copying the entire nbt again\n        Iterator<Map.Entry<String, Tag>> iterator = compoundTag.iterator();\n        boolean hasLongArrayTag = false;\n        while (iterator.hasNext()) {\n            Map.Entry<String, Tag> entry = iterator.next();\n            if (entry.getValue() instanceof CompoundTag tag) {\n                CompoundTag nestedBackupTag = new CompoundTag();\n                backupTag.put(entry.getKey(), nestedBackupTag);\n                hasLongArrayTag |= handleNbtToClient(tag, nestedBackupTag);\n            } else if (entry.getValue() instanceof LongArrayTag tag) {\n                backupTag.put(entry.getKey(), fromLongArrayTag(tag));\n                iterator.remove();\n                hasLongArrayTag = true;\n            }\n        }\n        return hasLongArrayTag;\n    }\n\n    @Override\n    public @Nullable Item handleItemToServer(UserConnection connection, Item item) {\n        if (item == null) return null;\n        item = super.handleItemToServer(connection, item);\n\n        if (item.tag() != null) {\n            if (item.tag().remove(\"Via|LongArrayTags\") instanceof CompoundTag tag) {\n                handleNbtToServer(item.tag(), tag);\n            }\n        }\n\n        return item;\n    }\n\n    private void handleNbtToServer(CompoundTag compoundTag, CompoundTag backupTag) {\n        // Restore the removed long array tags\n        for (Map.Entry<String, Tag> entry : backupTag) {\n            if (entry.getValue() instanceof CompoundTag) {\n                CompoundTag nestedTag = compoundTag.getCompoundTag(entry.getKey());\n                handleNbtToServer(nestedTag, (CompoundTag) entry.getValue());\n            } else {\n                compoundTag.put(entry.getKey(), fromIntArrayTag((IntArrayTag) entry.getValue()));\n            }\n        }\n    }\n\n    private IntArrayTag fromLongArrayTag(LongArrayTag tag) {\n        int[] intArray = new int[tag.length() * 2];\n        long[] longArray = tag.getValue();\n        int i = 0;\n        for (long l : longArray) {\n            intArray[i++] = (int) (l >> 32);\n            intArray[i++] = (int) l;\n        }\n        return new IntArrayTag(intArray);\n    }\n\n    private LongArrayTag fromIntArrayTag(IntArrayTag tag) {\n        long[] longArray = new long[tag.length() / 2];\n        int[] intArray = tag.getValue();\n        for (int i = 0, j = 0; i < intArray.length; i += 2, j++) {\n            longArray[j] = (long) intArray[i] << 32 | ((long) intArray[i + 1] & 0xFFFFFFFFL);\n        }\n        return new LongArrayTag(longArray);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_12to1_11_1/rewriter/ComponentRewriter1_12.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.viaversion.viabackwards.protocol.v1_12to1_11_1.rewriter;\n\nimport com.viaversion.viabackwards.api.rewriters.text.JsonNBTComponentRewriter;\nimport com.viaversion.viabackwards.protocol.v1_12to1_11_1.Protocol1_12To1_11_1;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.libs.gson.JsonElement;\nimport com.viaversion.viaversion.libs.gson.JsonObject;\nimport com.viaversion.viaversion.protocols.v1_11_1to1_12.packet.ClientboundPackets1_12;\nimport com.viaversion.viaversion.rewriter.text.ComponentRewriterBase;\n\npublic class ComponentRewriter1_12 extends JsonNBTComponentRewriter<ClientboundPackets1_12> {\n\n    public ComponentRewriter1_12(Protocol1_12To1_11_1 protocol) {\n        super(protocol, ComponentRewriterBase.ReadType.JSON);\n    }\n\n    @Override\n    public void processText(UserConnection connection, JsonElement element) {\n        super.processText(connection, element);\n        if (element == null || !element.isJsonObject()) {\n            return;\n        }\n\n        JsonObject object = element.getAsJsonObject();\n        JsonElement keybind = object.remove(\"keybind\");\n        if (keybind == null) {\n            return;\n        }\n\n        //TODO Add nicer text for the key, also use this component rewriter in more packets\n        object.addProperty(\"text\", keybind.getAsString());\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_12to1_11_1/rewriter/EntityPacketRewriter1_12.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.viaversion.viabackwards.protocol.v1_12to1_11_1.rewriter;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.viabackwards.api.rewriters.LegacyEntityRewriter;\nimport com.viaversion.viabackwards.protocol.v1_12to1_11_1.Protocol1_12To1_11_1;\nimport com.viaversion.viabackwards.protocol.v1_12to1_11_1.storage.ParrotStorage;\nimport com.viaversion.viabackwards.protocol.v1_12to1_11_1.storage.ShoulderTracker;\nimport com.viaversion.viaversion.api.data.entity.StoredEntityData;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityType;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_12;\nimport com.viaversion.viaversion.api.minecraft.entitydata.EntityData;\nimport com.viaversion.viaversion.api.minecraft.entitydata.types.EntityDataTypes1_12;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.libs.gson.JsonElement;\nimport com.viaversion.viaversion.protocols.v1_11_1to1_12.packet.ClientboundPackets1_12;\nimport com.viaversion.viaversion.protocols.v1_9_1to1_9_3.packet.ClientboundPackets1_9_3;\n\npublic class EntityPacketRewriter1_12 extends LegacyEntityRewriter<ClientboundPackets1_12, Protocol1_12To1_11_1> {\n\n    public EntityPacketRewriter1_12(Protocol1_12To1_11_1 protocol) {\n        super(protocol);\n    }\n\n    @Override\n    protected void registerPackets() {\n        protocol.registerClientbound(ClientboundPackets1_12.ADD_ENTITY, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT); // 0 - Entity id\n                map(Types.UUID); // 1 - UUID\n                map(Types.BYTE); // 2 - Type\n                map(Types.DOUBLE); // 3 - x\n                map(Types.DOUBLE); // 4 - y\n                map(Types.DOUBLE); // 5 - z\n                map(Types.BYTE); // 6 - Pitch\n                map(Types.BYTE); // 7 - Yaw\n                map(Types.INT); // 8 - data\n\n                // Track Entity\n                handler(getObjectTrackerHandler());\n                handler(getObjectRewriter(EntityTypes1_12.ObjectType::findById));\n\n                handler(protocol.getItemRewriter().getFallingBlockHandler());\n            }\n        });\n\n        registerTracker(ClientboundPackets1_12.ADD_EXPERIENCE_ORB, EntityTypes1_12.EntityType.EXPERIENCE_ORB);\n        registerTracker(ClientboundPackets1_12.ADD_GLOBAL_ENTITY, EntityTypes1_12.EntityType.LIGHTNING_BOLT);\n\n        protocol.registerClientbound(ClientboundPackets1_12.ADD_MOB, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT); // 0 - Entity id\n                map(Types.UUID); // 1 - UUID\n                map(Types.VAR_INT); // 2 - Entity Type\n                map(Types.DOUBLE); // 3 - X\n                map(Types.DOUBLE); // 4 - Y\n                map(Types.DOUBLE); // 5 - Z\n                map(Types.BYTE); // 6 - Yaw\n                map(Types.BYTE); // 7 - Pitch\n                map(Types.BYTE); // 8 - Head Pitch\n                map(Types.SHORT); // 9 - Velocity X\n                map(Types.SHORT); // 10 - Velocity Y\n                map(Types.SHORT); // 11 - Velocity Z\n                map(Types.ENTITY_DATA_LIST1_12, Types.ENTITY_DATA_LIST1_9); // 12 - Entity data\n\n                // Track entity\n                handler(getTrackerHandler());\n\n                // Rewrite entity type / data\n                handler(getMobSpawnRewriter1_11(Types.ENTITY_DATA_LIST1_9));\n            }\n        });\n\n        registerTracker(ClientboundPackets1_12.ADD_PAINTING, EntityTypes1_12.EntityType.PAINTING);\n\n        protocol.registerClientbound(ClientboundPackets1_12.ADD_PLAYER, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT); // 0 - Entity ID\n                map(Types.UUID); // 1 - Player UUID\n                map(Types.DOUBLE); // 2 - X\n                map(Types.DOUBLE); // 3 - Y\n                map(Types.DOUBLE); // 4 - Z\n                map(Types.BYTE); // 5 - Yaw\n                map(Types.BYTE); // 6 - Pitch\n                map(Types.ENTITY_DATA_LIST1_12, Types.ENTITY_DATA_LIST1_9); // 7 - Entity data list\n\n                handler(getTrackerAndDataHandler(Types.ENTITY_DATA_LIST1_9, EntityTypes1_12.EntityType.PLAYER));\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_12.LOGIN, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.INT); // 0 - Entity ID\n                map(Types.UNSIGNED_BYTE); // 1 - Gamemode\n                map(Types.INT); // 2 - Dimension\n\n                handler(getDimensionHandler(1));\n                handler(getPlayerTrackerHandler());\n\n                handler(wrapper -> {\n                    ShoulderTracker tracker = wrapper.user().get(ShoulderTracker.class);\n                    tracker.setEntityId(wrapper.get(Types.INT, 0));\n                });\n\n                // Send fake inventory achievement\n                handler(packetWrapper -> {\n                    PacketWrapper wrapper = PacketWrapper.create(ClientboundPackets1_9_3.AWARD_STATS, packetWrapper.user());\n\n                    wrapper.write(Types.VAR_INT, 1);\n                    wrapper.write(Types.STRING, \"achievement.openInventory\");\n                    wrapper.write(Types.VAR_INT, 1);\n\n                    wrapper.scheduleSend(Protocol1_12To1_11_1.class);\n                });\n            }\n        });\n\n        registerRespawn(ClientboundPackets1_12.RESPAWN);\n        registerRemoveEntities(ClientboundPackets1_12.REMOVE_ENTITIES);\n        registerSetEntityData(ClientboundPackets1_12.SET_ENTITY_DATA, Types.ENTITY_DATA_LIST1_12, Types.ENTITY_DATA_LIST1_9);\n\n        protocol.registerClientbound(ClientboundPackets1_12.UPDATE_ATTRIBUTES, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT);\n                map(Types.INT);\n                handler(wrapper -> {\n                    int size = wrapper.get(Types.INT, 0);\n                    int newSize = size;\n                    for (int i = 0; i < size; i++) {\n                        String key = wrapper.read(Types.STRING);\n                        // Remove new attribute\n                        if (key.equals(\"generic.flyingSpeed\")) {\n                            newSize--;\n                            wrapper.read(Types.DOUBLE);\n                            int modSize = wrapper.read(Types.VAR_INT);\n                            for (int j = 0; j < modSize; j++) {\n                                wrapper.read(Types.UUID);\n                                wrapper.read(Types.DOUBLE);\n                                wrapper.read(Types.BYTE);\n                            }\n                        } else {\n                            wrapper.write(Types.STRING, key);\n                            wrapper.passthrough(Types.DOUBLE);\n                            int modSize = wrapper.passthrough(Types.VAR_INT);\n                            for (int j = 0; j < modSize; j++) {\n                                wrapper.passthrough(Types.UUID);\n                                wrapper.passthrough(Types.DOUBLE);\n                                wrapper.passthrough(Types.BYTE);\n                            }\n                        }\n                    }\n\n                    if (newSize != size) {\n                        wrapper.set(Types.INT, 0, newSize);\n                    }\n                });\n            }\n        });\n    }\n\n    @Override\n    protected void registerRewrites() {\n        mapEntityTypeWithData(EntityTypes1_12.EntityType.PARROT, EntityTypes1_12.EntityType.BAT).plainName().spawnEntityData(storage -> storage.add(new EntityData(12, EntityDataTypes1_12.BYTE, (byte) 0x00)));\n        mapEntityTypeWithData(EntityTypes1_12.EntityType.ILLUSIONER, EntityTypes1_12.EntityType.EVOKER).plainName();\n\n        filter().handler((event, data) -> {\n            if (data.dataType() == EntityDataTypes1_12.COMPONENT) {\n                protocol.getComponentRewriter().processText(event.user(), (JsonElement) data.getValue());\n            }\n        });\n\n        // Handle Illager\n        filter().type(EntityTypes1_12.EntityType.EVOKER).removeIndex(12);\n\n        filter().type(EntityTypes1_12.EntityType.ILLUSIONER).index(0).handler((event, data) -> {\n            byte mask = (byte) data.getValue();\n\n            if ((mask & 0x20) == 0x20) {\n                mask &= ~0x20;\n            }\n\n            data.setValue(mask);\n        });\n\n        // Create Parrot storage\n        filter().type(EntityTypes1_12.EntityType.PARROT).handler((event, data) -> {\n            StoredEntityData entityData = storedEntityData(event);\n            if (!entityData.has(ParrotStorage.class)) {\n                entityData.put(new ParrotStorage());\n            }\n        });\n        // Parrot remove animal entity data\n        filter().type(EntityTypes1_12.EntityType.PARROT).cancel(12); // Is baby\n        filter().type(EntityTypes1_12.EntityType.PARROT).index(13).handler((event, data) -> {\n            StoredEntityData entityData = storedEntityData(event);\n            ParrotStorage storage = entityData.get(ParrotStorage.class);\n            boolean isSitting = (((byte) data.getValue()) & 0x01) == 0x01;\n            boolean isTamed = (((byte) data.getValue()) & 0x04) == 0x04;\n\n            if (!storage.isTamed() && isTamed) {\n                // TODO do something to let the user know it's done\n            }\n\n            storage.setTamed(isTamed);\n\n            if (isSitting) {\n                event.setIndex(12);\n                data.setValue((byte) 0x01);\n                storage.setSitting(true);\n            } else if (storage.isSitting()) {\n                event.setIndex(12);\n                data.setValue((byte) 0x00);\n                storage.setSitting(false);\n            } else {\n                event.cancel();\n            }\n        }); // Flags (Is sitting etc, might be useful in the future\n        filter().type(EntityTypes1_12.EntityType.PARROT).cancel(14); // Owner\n        filter().type(EntityTypes1_12.EntityType.PARROT).cancel(15); // Variant\n\n        // Left shoulder entity data\n        filter().type(EntityTypes1_12.EntityType.PLAYER).index(15).handler((event, data) -> {\n            CompoundTag tag = (CompoundTag) data.getValue();\n            ShoulderTracker tracker = event.user().get(ShoulderTracker.class);\n\n            if (tag.isEmpty() && tracker.getLeftShoulder() != null) {\n                tracker.setLeftShoulder(null);\n                tracker.update();\n            } else if (tag.getStringTag(\"id\") != null && event.entityId() == tracker.getEntityId()) {\n                String id = tag.getStringTag(\"id\").getValue();\n                if (tracker.getLeftShoulder() == null || !tracker.getLeftShoulder().equals(id)) {\n                    tracker.setLeftShoulder(id);\n                    tracker.update();\n                }\n            }\n\n            event.cancel();\n        });\n\n        // Right shoulder entity data\n        filter().type(EntityTypes1_12.EntityType.PLAYER).index(16).handler((event, data) -> {\n            CompoundTag tag = (CompoundTag) event.data().getValue();\n            ShoulderTracker tracker = event.user().get(ShoulderTracker.class);\n\n            if (tag.isEmpty() && tracker.getRightShoulder() != null) {\n                tracker.setRightShoulder(null);\n                tracker.update();\n            } else if (tag.getStringTag(\"id\") != null && event.entityId() == tracker.getEntityId()) {\n                String id = tag.getStringTag(\"id\").getValue();\n                if (tracker.getRightShoulder() == null || !tracker.getRightShoulder().equals(id)) {\n                    tracker.setRightShoulder(id);\n                    tracker.update();\n                }\n            }\n\n            event.cancel();\n        });\n    }\n\n    @Override\n    public EntityType typeFromId(int typeId) {\n        return EntityTypes1_12.EntityType.findById(typeId);\n    }\n\n    @Override\n    public EntityType objectTypeFromId(int typeId, int data) {\n        return EntityTypes1_12.ObjectType.getEntityType(typeId, data);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_12to1_11_1/rewriter/SoundPacketRewriter1_12.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.viaversion.viabackwards.protocol.v1_12to1_11_1.rewriter;\n\nimport com.viaversion.viabackwards.api.rewriters.LegacySoundRewriter;\nimport com.viaversion.viabackwards.protocol.v1_12to1_11_1.Protocol1_12To1_11_1;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.protocols.v1_11_1to1_12.packet.ClientboundPackets1_12;\n\npublic class SoundPacketRewriter1_12 extends LegacySoundRewriter<Protocol1_12To1_11_1> {\n\n    public SoundPacketRewriter1_12(Protocol1_12To1_11_1 protocol) {\n        super(protocol);\n    }\n\n    @Override\n    protected void registerPackets() {\n        protocol.replaceClientbound(ClientboundPackets1_12.SOUND, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT); // 0 - Sound name\n                map(Types.VAR_INT); // 1 - Sound Category\n                map(Types.INT); // 2 - x\n                map(Types.INT); // 3 - y\n                map(Types.INT); // 4 - z\n                map(Types.FLOAT); // 5 - Volume\n                map(Types.FLOAT); // 6 - Pitch\n\n                handler(wrapper -> {\n                    int oldId = wrapper.get(Types.VAR_INT, 0);\n                    int newId = handleSounds(oldId);\n                    if (newId == -1) {\n                        wrapper.cancel();\n                        return;\n                    }\n\n                    if (hasPitch(oldId)) {\n                        wrapper.set(Types.FLOAT, 1, handlePitch(oldId));\n                    }\n                    wrapper.set(Types.VAR_INT, 0, newId);\n                });\n            }\n        });\n    }\n\n\n    @Override\n    protected void registerRewrites() {\n        //TODO use the diff file to also have named sound remaps\n        // (there were *A LOT* of refactored names)\n\n        // Replacement sounds, suggestions are always welcome\n        // Automatically generated from PAaaS\n        added(26, 277, 1.4f); // block.end_portal.spawn -> entity.lightning.thunder\n        added(27, -1); // block.end_portal_frame.fill\n\n        added(72, 70); // block.note.bell -> block.note.harp\n        added(73, 70); // block.note.chime -> block.note.harp\n        added(74, 70); // block.note.flute -> block.note.harp\n        added(75, 70); // block.note.guitar -> block.note.harp\n        added(80, 70); // block.note.xylophone -> block.note.harp\n\n        added(150, -1); // entity.boat.paddle_land\n        added(151, -1); // entity.boat.paddle_water\n\n        added(152, -1); // entity.bobber.retrieve\n\n        added(195, -1); // entity.endereye.death\n\n        added(274, 198, 0.8f); // entity.illusion_illager.ambient -> entity.evocation_illager.ambient\n        added(275, 199, 0.8f); // entity.illusion_illager.cast_spell -> entity.evocation_illager.cast_spell\n        added(276, 200, 0.8f); // entity.illusion_illager.death -> entity.evocation_illager.death\n        added(277, 201, 0.8f); // entity.illusion_illager.hurt -> entity.evocation_illager.hurt\n        added(278, 191, 0.9f); // entity.illusion_illager.mirror_move -> entity.endermen.teleport\n        added(279, 203, 1.5f); // entity.illusion_illager.prepare_blindness -> entity.evocation_illager.prepare_summon\n        added(280, 202, 0.8f); // entity.illusion_illager.prepare_mirror -> entity.evocation_illager.prepare_attack\n\n        added(319, 133, 0.6f); // entity.parrot.ambient -> entity.bat.ambient\n        added(320, 134, 1.7f); // entity.parrot.death -> entity.bat.death\n        added(321, 219, 1.5f); // entity.parrot.eat -> entity.generic.eat\n        added(322, 136, 0.7f); // entity.parrot.fly -> entity.bat.loop\n        added(323, 135, 1.6f); // entity.parrot.hurt -> entity.bat.hurt\n        added(324, 138, 1.5f); // entity.parrot.imitate.blaze -> entity.blaze.ambient\n        added(325, 163, 1.5f); // entity.parrot.imitate.creeper -> entity.creeper.primed\n        added(326, 170, 1.5f); // entity.parrot.imitate.elder_guardian -> entity.elder_guardian.ambient\n        added(327, 178, 1.5f); // entity.parrot.imitate.enderdragon -> entity.enderdragon.ambient\n        added(328, 186, 1.5f); // entity.parrot.imitate.enderman -> entity.endermen.ambient\n        added(329, 192, 1.5f); // entity.parrot.imitate.endermite -> entity.endermite.ambient\n        added(330, 198, 1.5f); // entity.parrot.imitate.evocation_illager -> entity.evocation_illager.ambient\n        added(331, 226, 1.5f); // entity.parrot.imitate.ghast -> entity.ghast.ambient\n        added(332, 259, 1.5f); // entity.parrot.imitate.husk -> entity.husk.ambient\n        added(333, 198, 1.3f); // entity.parrot.imitate.illusion_illager -> entity.evocation_illager.ambient\n        added(334, 291, 1.5f); // entity.parrot.imitate.magmacube -> entity.magmacube.squish\n        added(335, 321, 1.5f); // entity.parrot.imitate.polar_bear -> entity.polar_bear.ambient\n        added(336, 337, 1.5f); // entity.parrot.imitate.shulker -> entity.shulker.ambient\n        added(337, 347, 1.5f); // entity.parrot.imitate.silverfish -> entity.silverfish.ambient\n        added(338, 351, 1.5f); // entity.parrot.imitate.skeleton -> entity.skeleton.ambient\n        added(339, 363, 1.5f); // entity.parrot.imitate.slime -> entity.slime.squish\n        added(340, 376, 1.5f); // entity.parrot.imitate.spider -> entity.spider.ambient\n        added(341, 385, 1.5f); // entity.parrot.imitate.stray -> entity.stray.ambient\n        added(342, 390, 1.5f); // entity.parrot.imitate.vex -> entity.vex.ambient\n        added(343, 400, 1.5f); // entity.parrot.imitate.vindication_illager -> entity.vindication_illager.ambient\n        added(344, 403, 1.5f); // entity.parrot.imitate.witch -> entity.witch.ambient\n        added(345, 408, 1.5f); // entity.parrot.imitate.wither -> entity.wither.ambient\n        added(346, 414, 1.5f); // entity.parrot.imitate.wither_skeleton -> entity.wither_skeleton.ambient\n        added(347, 418, 1.5f); // entity.parrot.imitate.wolf -> entity.wolf.ambient\n        added(348, 427, 1.5f); // entity.parrot.imitate.zombie -> entity.zombie.ambient\n        added(349, 438, 1.5f); // entity.parrot.imitate.zombie_pigman -> entity.zombie_pig.ambient\n        added(350, 442, 1.5f); // entity.parrot.imitate.zombie_villager -> entity.zombie_villager.ambient\n        added(351, 155); // entity.parrot.step -> entity.chicken.step\n\n        added(368, 316); // entity.player.hurt_drown -> entity.player.hurt\n        added(369, 316); // entity.player.hurt_on_fire -> entity.player.hurt\n\n        // No replacement sounds for these, since it could be confusing, the toast doesn't show up\n        added(544, -1); // ui.toast.in\n        added(545, -1); // ui.toast.out\n        added(546, 317, 1.5f); // ui.toast.challenge_complete\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_12to1_11_1/storage/ParrotStorage.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.viaversion.viabackwards.protocol.v1_12to1_11_1.storage;\n\npublic class ParrotStorage {\n    private boolean tamed = true;\n    private boolean sitting = true;\n\n    public boolean isTamed() {\n        return tamed;\n    }\n\n    public void setTamed(boolean tamed) {\n        this.tamed = tamed;\n    }\n\n    public boolean isSitting() {\n        return sitting;\n    }\n\n    public void setSitting(boolean sitting) {\n        this.sitting = sitting;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_12to1_11_1/storage/ShoulderTracker.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.viaversion.viabackwards.protocol.v1_12to1_11_1.storage;\n\nimport com.viaversion.viabackwards.ViaBackwards;\nimport com.viaversion.viabackwards.protocol.v1_12to1_11_1.Protocol1_12To1_11_1;\nimport com.viaversion.viaversion.api.connection.StoredObject;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.protocols.v1_11_1to1_12.packet.ClientboundPackets1_12;\nimport com.viaversion.viaversion.util.ComponentUtil;\nimport com.viaversion.viaversion.util.Key;\nimport java.util.Locale;\n\npublic class ShoulderTracker extends StoredObject {\n    private int entityId;\n    private String leftShoulder;\n    private String rightShoulder;\n\n    public ShoulderTracker(UserConnection user) {\n        super(user);\n    }\n\n    public void update() {\n        PacketWrapper wrapper = PacketWrapper.create(ClientboundPackets1_12.CHAT, getUser());\n\n        try {\n            wrapper.write(Types.COMPONENT, ComponentUtil.plainToJson(generateString()));\n        } catch (final Exception e) {\n            throw new RuntimeException(e);\n        }\n        wrapper.write(Types.BYTE, (byte) 2);\n\n        try {\n            wrapper.scheduleSend(Protocol1_12To1_11_1.class);\n        } catch (Exception e) {\n            ViaBackwards.getPlatform().getLogger().severe(\"Failed to send the shoulder indication\");\n            e.printStackTrace();\n        }\n    }\n\n    // Does actionbar not support json colors? :(\n    private String generateString() {\n        StringBuilder builder = new StringBuilder();\n\n        // Empty spaces because the non-json formatting is weird\n        builder.append(\"  \");\n        if (leftShoulder == null) {\n            builder.append(\"§4§lNothing\");\n        } else {\n            builder.append(\"§2§l\").append(getName(leftShoulder));\n        }\n\n        builder.append(\"§8§l <- §7§lShoulders§8§l -> \");\n\n        if (rightShoulder == null) {\n            builder.append(\"§4§lNothing\");\n        } else {\n            builder.append(\"§2§l\").append(getName(rightShoulder));\n        }\n\n        return builder.toString();\n    }\n\n    private String getName(String current) {\n        current = Key.stripMinecraftNamespace(current);\n\n        String[] array = current.split(\"_\");\n        StringBuilder builder = new StringBuilder();\n\n        for (String s : array) {\n            builder.append(s.substring(0, 1).toUpperCase(Locale.ROOT))\n                .append(s.substring(1))\n                .append(\" \");\n        }\n\n        return builder.toString();\n    }\n\n    public int getEntityId() {\n        return entityId;\n    }\n\n    public void setEntityId(int entityId) {\n        this.entityId = entityId;\n    }\n\n    public String getLeftShoulder() {\n        return leftShoulder;\n    }\n\n    public void setLeftShoulder(String leftShoulder) {\n        this.leftShoulder = leftShoulder;\n    }\n\n    public String getRightShoulder() {\n        return rightShoulder;\n    }\n\n    public void setRightShoulder(String rightShoulder) {\n        this.rightShoulder = rightShoulder;\n    }\n\n    @Override\n    public String toString() {\n        return \"ShoulderTracker{\" + \"entityId=\" + entityId + \", leftShoulder='\" + leftShoulder + '\\'' + \", rightShoulder='\" + rightShoulder + '\\'' + '}';\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_13_1to1_13/Protocol1_13_1To1_13.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_13_1to1_13;\n\nimport com.viaversion.viabackwards.ViaBackwards;\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viabackwards.api.data.BackwardsMappingData;\nimport com.viaversion.viabackwards.api.rewriters.text.JsonNBTComponentRewriter;\nimport com.viaversion.viabackwards.protocol.v1_13_1to1_13.rewriter.CommandRewriter1_13_1;\nimport com.viaversion.viabackwards.protocol.v1_13_1to1_13.rewriter.EntityPacketRewriter1_13_1;\nimport com.viaversion.viabackwards.protocol.v1_13_1to1_13.rewriter.ItemPacketRewriter1_13_1;\nimport com.viaversion.viabackwards.protocol.v1_13_1to1_13.rewriter.WorldPacketRewriter1_13_1;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.ClientWorld;\nimport com.viaversion.viaversion.api.minecraft.RegistryType;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_13;\nimport com.viaversion.viaversion.api.minecraft.item.Item;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.protocol.remapper.ValueTransformer;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.data.entity.EntityTrackerBase;\nimport com.viaversion.viaversion.libs.gson.JsonElement;\nimport com.viaversion.viaversion.libs.gson.JsonObject;\nimport com.viaversion.viaversion.protocols.v1_12_2to1_13.packet.ClientboundPackets1_13;\nimport com.viaversion.viaversion.protocols.v1_12_2to1_13.packet.ServerboundPackets1_13;\nimport com.viaversion.viaversion.protocols.v1_13to1_13_1.Protocol1_13To1_13_1;\nimport com.viaversion.viaversion.rewriter.ParticleRewriter;\nimport com.viaversion.viaversion.rewriter.TagRewriter;\nimport com.viaversion.viaversion.rewriter.text.ComponentRewriterBase;\nimport com.viaversion.viaversion.util.ComponentUtil;\n\npublic class Protocol1_13_1To1_13 extends BackwardsProtocol<ClientboundPackets1_13, ClientboundPackets1_13, ServerboundPackets1_13, ServerboundPackets1_13> {\n\n    public static final BackwardsMappingData MAPPINGS = new BackwardsMappingData(\"1.13.2\", \"1.13\", Protocol1_13To1_13_1.class);\n    private final EntityPacketRewriter1_13_1 entityRewriter = new EntityPacketRewriter1_13_1(this);\n    private final ItemPacketRewriter1_13_1 itemRewriter = new ItemPacketRewriter1_13_1(this);\n    private final ParticleRewriter<ClientboundPackets1_13> particleRewriter = new ParticleRewriter<>(this);\n    private final JsonNBTComponentRewriter<ClientboundPackets1_13> translatableRewriter = new JsonNBTComponentRewriter<>(this, ComponentRewriterBase.ReadType.JSON);\n    private final TagRewriter<ClientboundPackets1_13> tagRewriter = new TagRewriter<>(this);\n\n    public Protocol1_13_1To1_13() {\n        super(ClientboundPackets1_13.class, ClientboundPackets1_13.class, ServerboundPackets1_13.class, ServerboundPackets1_13.class);\n    }\n\n    @Override\n    protected void registerPackets() {\n        super.registerPackets();\n\n        WorldPacketRewriter1_13_1.register(this);\n\n        new CommandRewriter1_13_1(this).registerDeclareCommands(ClientboundPackets1_13.COMMANDS);\n\n        registerServerbound(ServerboundPackets1_13.COMMAND_SUGGESTION, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT);\n                map(Types.STRING, new ValueTransformer<>(Types.STRING) {\n                    @Override\n                    public String transform(PacketWrapper wrapper, String inputValue) {\n                        // 1.13 starts sending slash at start, so we remove it for compatibility\n                        return !inputValue.startsWith(\"/\") ? \"/\" + inputValue : inputValue;\n                    }\n                });\n            }\n        });\n\n        registerServerbound(ServerboundPackets1_13.EDIT_BOOK, wrapper -> {\n            final Item item = itemRewriter.handleItemToServer(wrapper.user(), wrapper.read(Types.ITEM1_13));\n            wrapper.write(Types.ITEM1_13, item);\n            wrapper.passthrough(Types.BOOLEAN);\n            wrapper.write(Types.VAR_INT, 0);\n        });\n\n        registerClientbound(ClientboundPackets1_13.OPEN_SCREEN, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.UNSIGNED_BYTE); // Id\n                map(Types.STRING); // Window Type\n                handler(wrapper -> {\n                    JsonElement title = wrapper.passthrough(Types.COMPONENT);\n                    translatableRewriter.processText(wrapper.user(), title);\n\n                    if (ViaBackwards.getConfig().fix1_13FormattedInventoryTitle()) {\n                        if (title.isJsonObject() && title.getAsJsonObject().size() == 1\n                            && title.getAsJsonObject().has(\"translate\")) {\n                            // Hotfix simple translatable components from being converted to legacy text\n                            return;\n                        }\n\n                        // https://bugs.mojang.com/browse/MC-124543\n                        JsonObject legacyComponent = new JsonObject();\n                        legacyComponent.addProperty(\"text\", ComponentUtil.jsonToLegacy(title));\n                        wrapper.set(Types.COMPONENT, 0, legacyComponent);\n                    }\n                });\n            }\n        });\n\n        registerClientbound(ClientboundPackets1_13.COMMAND_SUGGESTIONS, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT); // Transaction id\n                map(Types.VAR_INT); // Start\n                map(Types.VAR_INT); // Length\n                map(Types.VAR_INT); // Count\n                handler(wrapper -> {\n                    int start = wrapper.get(Types.VAR_INT, 1);\n                    wrapper.set(Types.VAR_INT, 1, start - 1); // Offset by +1 to take into account / at beginning\n                    // Passthrough suggestions\n                    int count = wrapper.get(Types.VAR_INT, 3);\n                    for (int i = 0; i < count; i++) {\n                        wrapper.passthrough(Types.STRING);\n                        wrapper.passthrough(Types.OPTIONAL_COMPONENT); // Tooltip\n                    }\n                });\n            }\n        });\n\n        replaceClientbound(ClientboundPackets1_13.BOSS_EVENT, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.UUID);\n                map(Types.VAR_INT);\n                handler(wrapper -> {\n                    int action = wrapper.get(Types.VAR_INT, 0);\n                    if (action == 0 || action == 3) {\n                        translatableRewriter.processText(wrapper.user(), wrapper.passthrough(Types.COMPONENT));\n                        if (action == 0) {\n                            wrapper.passthrough(Types.FLOAT);\n                            wrapper.passthrough(Types.VAR_INT);\n                            wrapper.passthrough(Types.VAR_INT);\n                            short flags = wrapper.read(Types.UNSIGNED_BYTE);\n                            if ((flags & 0x04) != 0) flags |= 0x02;\n                            wrapper.write(Types.UNSIGNED_BYTE, flags);\n                        }\n                    }\n                });\n            }\n        });\n\n        tagRewriter.register(ClientboundPackets1_13.UPDATE_TAGS, RegistryType.ITEM);\n    }\n\n    @Override\n    public void init(UserConnection user) {\n        user.addEntityTracker(getClass(), new EntityTrackerBase(user, EntityTypes1_13.EntityType.PLAYER));\n        user.addClientWorld(getClass(), new ClientWorld());\n    }\n\n    @Override\n    public BackwardsMappingData getMappingData() {\n        return MAPPINGS;\n    }\n\n    @Override\n    public EntityPacketRewriter1_13_1 getEntityRewriter() {\n        return entityRewriter;\n    }\n\n    @Override\n    public ItemPacketRewriter1_13_1 getItemRewriter() {\n        return itemRewriter;\n    }\n\n    @Override\n    public ParticleRewriter<ClientboundPackets1_13> getParticleRewriter() {\n        return particleRewriter;\n    }\n\n    @Override\n    public JsonNBTComponentRewriter<ClientboundPackets1_13> getComponentRewriter() {\n        return translatableRewriter;\n    }\n\n    public JsonNBTComponentRewriter<ClientboundPackets1_13> translatableRewriter() {\n        return translatableRewriter;\n    }\n\n    @Override\n    public TagRewriter<ClientboundPackets1_13> getTagRewriter() {\n        return tagRewriter;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_13_1to1_13/rewriter/CommandRewriter1_13_1.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_13_1to1_13.rewriter;\n\nimport com.viaversion.viabackwards.protocol.v1_13_1to1_13.Protocol1_13_1To1_13;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.protocols.v1_12_2to1_13.packet.ClientboundPackets1_13;\nimport com.viaversion.viaversion.rewriter.CommandRewriter;\nimport org.checkerframework.checker.nullness.qual.Nullable;\n\npublic class CommandRewriter1_13_1 extends CommandRewriter<ClientboundPackets1_13> {\n\n    public CommandRewriter1_13_1(Protocol1_13_1To1_13 protocol) {\n        super(protocol);\n\n        this.parserHandlers.put(\"minecraft:dimension\", wrapper -> wrapper.write(Types.VAR_INT, 0)); // Single word\n    }\n\n    @Override\n    public @Nullable String handleArgumentType(String argumentType) {\n        if (argumentType.equals(\"minecraft:column_pos\")) {\n            return \"minecraft:vec2\";\n        } else if (argumentType.equals(\"minecraft:dimension\")) {\n            return \"brigadier:string\";\n        }\n        return super.handleArgumentType(argumentType);\n    }\n\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_13_1to1_13/rewriter/EntityPacketRewriter1_13_1.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_13_1to1_13.rewriter;\n\nimport com.viaversion.viabackwards.api.rewriters.LegacyEntityRewriter;\nimport com.viaversion.viabackwards.protocol.v1_13_1to1_13.Protocol1_13_1To1_13;\nimport com.viaversion.viaversion.api.minecraft.Particle;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityType;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_13;\nimport com.viaversion.viaversion.api.minecraft.entitydata.EntityData;\nimport com.viaversion.viaversion.api.minecraft.item.Item;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.version.Types1_13;\nimport com.viaversion.viaversion.libs.gson.JsonElement;\nimport com.viaversion.viaversion.protocols.v1_12_2to1_13.packet.ClientboundPackets1_13;\nimport java.util.List;\n\npublic class EntityPacketRewriter1_13_1 extends LegacyEntityRewriter<ClientboundPackets1_13, Protocol1_13_1To1_13> {\n\n    public EntityPacketRewriter1_13_1(Protocol1_13_1To1_13 protocol) {\n        super(protocol);\n    }\n\n    @Override\n    protected void registerPackets() {\n        protocol.registerClientbound(ClientboundPackets1_13.ADD_ENTITY, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT); // 0 - Entity id\n                map(Types.UUID); // 1 - UUID\n                map(Types.BYTE); // 2 - Type\n                map(Types.DOUBLE); // 3 - X\n                map(Types.DOUBLE); // 4 - Y\n                map(Types.DOUBLE); // 5 - Z\n                map(Types.BYTE); // 6 - Pitch\n                map(Types.BYTE); // 7 - Yaw\n                map(Types.INT); // 8 - Data\n\n                handler(wrapper -> {\n                    int entityId = wrapper.get(Types.VAR_INT, 0);\n                    byte type = wrapper.get(Types.BYTE, 0);\n                    int data = wrapper.get(Types.INT, 0);\n                    EntityTypes1_13.EntityType entType = EntityTypes1_13.ObjectType.getEntityType(type, data);\n                    if (entType == null) {\n                        return;\n                    }\n\n                    // Rewrite falling block\n                    if (entType.is(EntityTypes1_13.EntityType.FALLING_BLOCK)) {\n                        wrapper.set(Types.INT, 0, protocol.getMappingData().getNewBlockStateId(data));\n                    }\n\n                    // Track Entity\n                    tracker(wrapper.user()).addEntity(entityId, entType);\n                });\n            }\n        });\n\n        registerTracker(ClientboundPackets1_13.ADD_EXPERIENCE_ORB, EntityTypes1_13.EntityType.EXPERIENCE_ORB);\n        registerTracker(ClientboundPackets1_13.ADD_GLOBAL_ENTITY, EntityTypes1_13.EntityType.LIGHTNING_BOLT);\n\n        protocol.registerClientbound(ClientboundPackets1_13.ADD_MOB, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT); // 0 - Entity ID\n                map(Types.UUID); // 1 - Entity UUID\n                map(Types.VAR_INT); // 2 - Entity Type\n                map(Types.DOUBLE); // 3 - X\n                map(Types.DOUBLE); // 4 - Y\n                map(Types.DOUBLE); // 5 - Z\n                map(Types.BYTE); // 6 - Yaw\n                map(Types.BYTE); // 7 - Pitch\n                map(Types.BYTE); // 8 - Head Pitch\n                map(Types.SHORT); // 9 - Velocity X\n                map(Types.SHORT); // 10 - Velocity Y\n                map(Types.SHORT); // 11 - Velocity Z\n                map(Types1_13.ENTITY_DATA_LIST); // 12 - Entity data\n\n                // Track Entity\n                handler(getTrackerHandler());\n\n                // Rewrite Entity data\n                handler(wrapper -> {\n                    List<EntityData> entityDataList = wrapper.get(Types1_13.ENTITY_DATA_LIST, 0);\n                    handleEntityData(wrapper.get(Types.VAR_INT, 0), entityDataList, wrapper.user());\n                });\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_13.ADD_PLAYER, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT); // 0 - Entity ID\n                map(Types.UUID); // 1 - Player UUID\n                map(Types.DOUBLE); // 2 - X\n                map(Types.DOUBLE); // 3 - Y\n                map(Types.DOUBLE); // 4 - Z\n                map(Types.BYTE); // 5 - Yaw\n                map(Types.BYTE); // 6 - Pitch\n                map(Types1_13.ENTITY_DATA_LIST); // 7 - Entity data\n\n                handler(getTrackerAndDataHandler(Types1_13.ENTITY_DATA_LIST, EntityTypes1_13.EntityType.PLAYER));\n            }\n        });\n\n        registerTracker(ClientboundPackets1_13.ADD_PAINTING, EntityTypes1_13.EntityType.PAINTING);\n        registerJoinGame(ClientboundPackets1_13.LOGIN, EntityTypes1_13.EntityType.PLAYER);\n        registerRespawn(ClientboundPackets1_13.RESPAWN);\n        registerSetEntityData(ClientboundPackets1_13.SET_ENTITY_DATA, Types1_13.ENTITY_DATA_LIST);\n    }\n\n    @Override\n    protected void registerRewrites() {\n        // Rewrite items & blocks\n        filter().handler((event, data) -> {\n            if (data.dataType() == Types1_13.ENTITY_DATA_TYPES.itemType) {\n                data.setValue(protocol.getItemRewriter().handleItemToClient(event.user(), (Item) data.getValue()));\n            } else if (data.dataType() == Types1_13.ENTITY_DATA_TYPES.optionalBlockStateType) {\n                // Convert to new block id\n                int value = (int) data.getValue();\n                data.setValue(protocol.getMappingData().getNewBlockStateId(value));\n            } else if (data.dataType() == Types1_13.ENTITY_DATA_TYPES.particleType) {\n                protocol.getParticleRewriter().rewriteParticle(event.user(), (Particle) data.getValue());\n            } else if (data.dataType() == Types1_13.ENTITY_DATA_TYPES.optionalComponentType || data.dataType() == Types1_13.ENTITY_DATA_TYPES.componentType) {\n                JsonElement element = data.value();\n                protocol.translatableRewriter().processText(event.user(), element);\n            }\n        });\n\n        // Remove shooter UUID\n        filter().type(EntityTypes1_13.EntityType.ABSTRACT_ARROW).cancel(7);\n\n        // Move colors to old position\n        filter().type(EntityTypes1_13.EntityType.SPECTRAL_ARROW).index(8).toIndex(7);\n\n        // Move loyalty level to old position\n        filter().type(EntityTypes1_13.EntityType.TRIDENT).index(8).toIndex(7);\n\n        // Rewrite Minecart blocks\n        registerBlockStateHandler(EntityTypes1_13.EntityType.ABSTRACT_MINECART, 9);\n    }\n\n    @Override\n    public EntityType typeFromId(int typeId) {\n        return EntityTypes1_13.EntityType.findById(typeId);\n    }\n\n    @Override\n    public EntityType objectTypeFromId(int typeId, int data) {\n        return EntityTypes1_13.ObjectType.getEntityType(typeId, data);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_13_1to1_13/rewriter/ItemPacketRewriter1_13_1.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_13_1to1_13.rewriter;\n\nimport com.viaversion.viabackwards.protocol.v1_13_1to1_13.Protocol1_13_1To1_13;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.protocols.v1_12_2to1_13.packet.ClientboundPackets1_13;\nimport com.viaversion.viaversion.protocols.v1_12_2to1_13.packet.ServerboundPackets1_13;\nimport com.viaversion.viaversion.rewriter.ItemRewriter;\n\npublic class ItemPacketRewriter1_13_1 extends ItemRewriter<ClientboundPackets1_13, ServerboundPackets1_13, Protocol1_13_1To1_13> {\n\n    public ItemPacketRewriter1_13_1(Protocol1_13_1To1_13 protocol) {\n        super(protocol, Types.ITEM1_13, Types.ITEM1_13_SHORT_ARRAY);\n    }\n\n    @Override\n    public void registerPackets() {\n        protocol.registerClientbound(ClientboundPackets1_13.CUSTOM_PAYLOAD, wrapper -> {\n            String channel = wrapper.passthrough(Types.STRING);\n            if (channel.equals(\"minecraft:trader_list\")) {\n                wrapper.passthrough(Types.INT); //Passthrough Window ID\n\n                int size = wrapper.passthrough(Types.UNSIGNED_BYTE);\n                for (int i = 0; i < size; i++) {\n                    passthroughClientboundItem(wrapper); // Input\n                    passthroughClientboundItem(wrapper); // Output\n\n                    boolean secondItem = wrapper.passthrough(Types.BOOLEAN); //Has second item\n                    if (secondItem) {\n                        passthroughClientboundItem(wrapper); // Second item\n                    }\n\n                    wrapper.passthrough(Types.BOOLEAN); //Trade disabled\n                    wrapper.passthrough(Types.INT); //Number of tools uses\n                    wrapper.passthrough(Types.INT); //Maximum number of trade uses\n                }\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_13_1to1_13/rewriter/WorldPacketRewriter1_13_1.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_13_1to1_13.rewriter;\n\nimport com.viaversion.viabackwards.protocol.v1_13_1to1_13.Protocol1_13_1To1_13;\nimport com.viaversion.viaversion.api.minecraft.BlockFace;\nimport com.viaversion.viaversion.api.minecraft.BlockPosition;\nimport com.viaversion.viaversion.api.minecraft.ClientWorld;\nimport com.viaversion.viaversion.api.minecraft.chunks.Chunk;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.chunk.ChunkType1_13;\nimport com.viaversion.viaversion.protocols.v1_12_2to1_13.packet.ClientboundPackets1_13;\nimport com.viaversion.viaversion.rewriter.BlockRewriter;\n\npublic class WorldPacketRewriter1_13_1 {\n\n    public static void register(Protocol1_13_1To1_13 protocol) {\n        BlockRewriter<ClientboundPackets1_13> blockRewriter = BlockRewriter.legacy(protocol);\n\n        protocol.registerClientbound(ClientboundPackets1_13.LEVEL_CHUNK, wrapper -> {\n            ClientWorld clientWorld = wrapper.user().getClientWorld(Protocol1_13_1To1_13.class);\n            Chunk chunk = wrapper.passthrough(ChunkType1_13.forEnvironment(clientWorld.getEnvironment()));\n\n            blockRewriter.handleChunk(chunk);\n        });\n\n        blockRewriter.registerBlockEvent(ClientboundPackets1_13.BLOCK_EVENT);\n        blockRewriter.registerBlockUpdate(ClientboundPackets1_13.BLOCK_UPDATE);\n        blockRewriter.registerChunkBlocksUpdate(ClientboundPackets1_13.CHUNK_BLOCKS_UPDATE);\n        protocol.registerClientbound(ClientboundPackets1_13.LEVEL_EVENT, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.INT); // Effect Id\n                map(Types.BLOCK_POSITION1_8); // Location\n                map(Types.INT); // Data\n                handler(wrapper -> {\n                    int id = wrapper.get(Types.INT, 0);\n                    int data = wrapper.get(Types.INT, 1);\n                    if (id == 1010) { // Play record\n                        wrapper.set(Types.INT, 1, protocol.getMappingData().getNewItemId(data));\n                    } else if (id == 2001) { // Block break + block break sound\n                        wrapper.set(Types.INT, 1, protocol.getMappingData().getNewBlockStateId(data));\n                    } else if (id == 2000) { // Smoke\n                        switch (data) { // Down\n                            case 0, 1 -> { // Up\n                                BlockPosition pos = wrapper.get(Types.BLOCK_POSITION1_8, 0);\n                                BlockFace relative = data == 0 ? BlockFace.BOTTOM : BlockFace.TOP;\n                                wrapper.set(Types.BLOCK_POSITION1_8, 0, pos.getRelative(relative)); // Y Offset\n                                wrapper.set(Types.INT, 1, 4); // Self\n                            }\n                            case 2 -> wrapper.set(Types.INT, 1, 1); // North\n                            case 3 -> wrapper.set(Types.INT, 1, 7); // South\n                            case 4 -> wrapper.set(Types.INT, 1, 3); // West\n                            case 5 -> wrapper.set(Types.INT, 1, 5); // East\n                        }\n                    }\n                });\n            }\n        });\n\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_13_2to1_13_1/Protocol1_13_2To1_13_1.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_13_2to1_13_1;\n\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viabackwards.protocol.v1_13_2to1_13_1.rewriter.EntityPacketRewriter1_13_2;\nimport com.viaversion.viabackwards.protocol.v1_13_2to1_13_1.rewriter.ItemPacketRewriter1_13_2;\nimport com.viaversion.viabackwards.protocol.v1_13_2to1_13_1.rewriter.WorldPacketRewriter1_13_2;\nimport com.viaversion.viaversion.api.minecraft.item.Item;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.protocols.v1_12_2to1_13.packet.ClientboundPackets1_13;\nimport com.viaversion.viaversion.protocols.v1_12_2to1_13.packet.ServerboundPackets1_13;\n\npublic class Protocol1_13_2To1_13_1 extends BackwardsProtocol<ClientboundPackets1_13, ClientboundPackets1_13, ServerboundPackets1_13, ServerboundPackets1_13> {\n\n    public Protocol1_13_2To1_13_1() {\n        super(ClientboundPackets1_13.class, ClientboundPackets1_13.class, ServerboundPackets1_13.class, ServerboundPackets1_13.class);\n    }\n\n    @Override\n    protected void registerPackets() {\n        ItemPacketRewriter1_13_2.register(this);\n        WorldPacketRewriter1_13_2.register(this);\n        EntityPacketRewriter1_13_2.register(this);\n\n        registerServerbound(ServerboundPackets1_13.EDIT_BOOK, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.ITEM1_13, Types.ITEM1_13_2);\n            }\n        });\n\n        registerClientbound(ClientboundPackets1_13.UPDATE_ADVANCEMENTS, wrapper -> {\n            wrapper.passthrough(Types.BOOLEAN); // Reset/clear\n            int size = wrapper.passthrough(Types.VAR_INT); // Mapping size\n\n            for (int i = 0; i < size; i++) {\n                wrapper.passthrough(Types.STRING); // Identifier\n                wrapper.passthrough(Types.OPTIONAL_STRING); // Parent\n\n                // Display data\n                if (wrapper.passthrough(Types.BOOLEAN)) {\n                    wrapper.passthrough(Types.COMPONENT); // Title\n                    wrapper.passthrough(Types.COMPONENT); // Description\n                    Item icon = wrapper.read(Types.ITEM1_13_2);\n                    wrapper.write(Types.ITEM1_13, icon);\n                    wrapper.passthrough(Types.VAR_INT); // Frame type\n                    int flags = wrapper.passthrough(Types.INT); // Flags\n                    if ((flags & 1) != 0)\n                        wrapper.passthrough(Types.STRING); // Background texture\n                    wrapper.passthrough(Types.FLOAT); // X\n                    wrapper.passthrough(Types.FLOAT); // Y\n                }\n\n                wrapper.passthrough(Types.STRING_ARRAY); // Criteria\n\n                int arrayLength = wrapper.passthrough(Types.VAR_INT);\n                for (int array = 0; array < arrayLength; array++) {\n                    wrapper.passthrough(Types.STRING_ARRAY); // String array\n                }\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_13_2to1_13_1/rewriter/EntityPacketRewriter1_13_2.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_13_2to1_13_1.rewriter;\n\nimport com.viaversion.viabackwards.protocol.v1_13_2to1_13_1.Protocol1_13_2To1_13_1;\nimport com.viaversion.viaversion.api.minecraft.Particle;\nimport com.viaversion.viaversion.api.minecraft.entitydata.EntityData;\nimport com.viaversion.viaversion.api.minecraft.entitydata.EntityDataType;\nimport com.viaversion.viaversion.api.minecraft.item.Item;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.version.Types1_13;\nimport com.viaversion.viaversion.api.type.types.version.Types1_13_2;\nimport com.viaversion.viaversion.protocols.v1_12_2to1_13.packet.ClientboundPackets1_13;\n\npublic class EntityPacketRewriter1_13_2 {\n\n\n    public static void register(Protocol1_13_2To1_13_1 protocol) {\n        protocol.registerClientbound(ClientboundPackets1_13.ADD_MOB, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT); // 0 - Entity ID\n                map(Types.UUID); // 1 - Entity UUID\n                map(Types.VAR_INT); // 2 - Entity Type\n                map(Types.DOUBLE); // 3 - X\n                map(Types.DOUBLE); // 4 - Y\n                map(Types.DOUBLE); // 5 - Z\n                map(Types.BYTE); // 6 - Yaw\n                map(Types.BYTE); // 7 - Pitch\n                map(Types.BYTE); // 8 - Head Pitch\n                map(Types.SHORT); // 9 - Velocity X\n                map(Types.SHORT); // 10 - Velocity Y\n                map(Types.SHORT); // 11 - Velocity Z\n                map(Types1_13_2.ENTITY_DATA_LIST, Types1_13.ENTITY_DATA_LIST); // 12 - Entity data\n\n                handler(EntityPacketRewriter1_13_2::updateEntityData);\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_13.ADD_PLAYER, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT); // 0 - Entity ID\n                map(Types.UUID); // 1 - Player UUID\n                map(Types.DOUBLE); // 2 - X\n                map(Types.DOUBLE); // 3 - Y\n                map(Types.DOUBLE); // 4 - Z\n                map(Types.BYTE); // 5 - Yaw\n                map(Types.BYTE); // 6 - Pitch\n                map(Types1_13_2.ENTITY_DATA_LIST, Types1_13.ENTITY_DATA_LIST); // 7 - Entity data\n\n                handler(EntityPacketRewriter1_13_2::updateEntityData);\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_13.SET_ENTITY_DATA, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT); // 0 - Entity ID\n                map(Types1_13_2.ENTITY_DATA_LIST, Types1_13.ENTITY_DATA_LIST); // 1 - Entity data list\n\n                handler(EntityPacketRewriter1_13_2::updateEntityData);\n            }\n        });\n    }\n\n    private static void updateEntityData(final PacketWrapper wrapper) {\n        for (final EntityData data : wrapper.get(Types1_13.ENTITY_DATA_LIST, 0)) {\n            final EntityDataType dataType = Types1_13.ENTITY_DATA_TYPES.byId(data.dataType().typeId());\n            data.setDataType(dataType);\n\n            if (dataType == Types1_13.ENTITY_DATA_TYPES.particleType) {\n                final Particle particle = data.value();\n                if (particle.id() == 27) {\n                    final Item item = particle.<Item>getArgument(0).getValue();\n                    particle.set(0, Types.ITEM1_13, item);\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_13_2to1_13_1/rewriter/ItemPacketRewriter1_13_2.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_13_2to1_13_1.rewriter;\n\nimport com.viaversion.viabackwards.protocol.v1_13_2to1_13_1.Protocol1_13_2To1_13_1;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.protocols.v1_12_2to1_13.packet.ClientboundPackets1_13;\nimport com.viaversion.viaversion.protocols.v1_12_2to1_13.packet.ServerboundPackets1_13;\n\npublic class ItemPacketRewriter1_13_2 {\n\n    public static void register(Protocol1_13_2To1_13_1 protocol) {\n        protocol.registerClientbound(ClientboundPackets1_13.CONTAINER_SET_SLOT, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.BYTE); // 0 - Window ID\n                map(Types.SHORT); // 1 - Slot ID\n                map(Types.ITEM1_13_2, Types.ITEM1_13); // 2 - Slot Value\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_13.CONTAINER_SET_CONTENT, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.UNSIGNED_BYTE); // 0 - Window ID\n                map(Types.ITEM1_13_2_SHORT_ARRAY, Types.ITEM1_13_SHORT_ARRAY); // 1 - Window Values\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_13.CUSTOM_PAYLOAD, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.STRING); // Channel\n                handler(wrapper -> {\n                    String channel = wrapper.get(Types.STRING, 0);\n                    if (channel.equals(\"minecraft:trader_list\") || channel.equals(\"trader_list\")) {\n                        wrapper.passthrough(Types.INT); // Passthrough Window ID\n\n                        int size = wrapper.passthrough(Types.UNSIGNED_BYTE);\n                        for (int i = 0; i < size; i++) {\n                            // Input Item\n                            wrapper.write(Types.ITEM1_13, wrapper.read(Types.ITEM1_13_2));\n                            // Output Item\n                            wrapper.write(Types.ITEM1_13, wrapper.read(Types.ITEM1_13_2));\n\n                            boolean secondItem = wrapper.passthrough(Types.BOOLEAN); // Has second item\n                            if (secondItem) {\n                                wrapper.write(Types.ITEM1_13, wrapper.read(Types.ITEM1_13_2));\n                            }\n\n                            wrapper.passthrough(Types.BOOLEAN); // Trade disabled\n                            wrapper.passthrough(Types.INT); // Number of tools uses\n                            wrapper.passthrough(Types.INT); // Maximum number of trade uses\n                        }\n                    }\n                });\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_13.SET_EQUIPPED_ITEM, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT); // 0 - Entity ID\n                map(Types.VAR_INT); // 1 - Slot ID\n                map(Types.ITEM1_13_2, Types.ITEM1_13); // 2 - Item\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_13.UPDATE_RECIPES, wrapper -> {\n            int recipesNo = wrapper.passthrough(Types.VAR_INT);\n            for (int i = 0; i < recipesNo; i++) {\n                wrapper.passthrough(Types.STRING); // Id\n                String type = wrapper.passthrough(Types.STRING);\n                if (type.equals(\"crafting_shapeless\")) {\n                    wrapper.passthrough(Types.STRING); // Group\n                    int ingredientsNo = wrapper.passthrough(Types.VAR_INT);\n                    for (int i1 = 0; i1 < ingredientsNo; i1++) {\n                        wrapper.write(Types.ITEM1_13_ARRAY, wrapper.read(Types.ITEM1_13_2_ARRAY));\n                    }\n                    wrapper.write(Types.ITEM1_13, wrapper.read(Types.ITEM1_13_2));\n                } else if (type.equals(\"crafting_shaped\")) {\n                    int ingredientsNo = wrapper.passthrough(Types.VAR_INT) * wrapper.passthrough(Types.VAR_INT);\n                    wrapper.passthrough(Types.STRING); // Group\n                    for (int i1 = 0; i1 < ingredientsNo; i1++) {\n                        wrapper.write(Types.ITEM1_13_ARRAY, wrapper.read(Types.ITEM1_13_2_ARRAY));\n                    }\n                    wrapper.write(Types.ITEM1_13, wrapper.read(Types.ITEM1_13_2));\n                } else if (type.equals(\"smelting\")) {\n                    wrapper.passthrough(Types.STRING); // Group\n                    // Ingredient start\n                    wrapper.write(Types.ITEM1_13_ARRAY, wrapper.read(Types.ITEM1_13_2_ARRAY));\n                    // Ingredient end\n                    wrapper.write(Types.ITEM1_13, wrapper.read(Types.ITEM1_13_2));\n                    wrapper.passthrough(Types.FLOAT); // EXP\n                    wrapper.passthrough(Types.VAR_INT); // Cooking time\n                }\n            }\n        });\n\n        protocol.registerServerbound(ServerboundPackets1_13.CONTAINER_CLICK, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.BYTE); // 0 - Window ID\n                map(Types.SHORT); // 1 - Slot\n                map(Types.BYTE); // 2 - Button\n                map(Types.SHORT); // 3 - Action number\n                map(Types.VAR_INT); // 4 - Mode\n                map(Types.ITEM1_13, Types.ITEM1_13_2); // 5 - Clicked Item\n            }\n        });\n\n        protocol.registerServerbound(ServerboundPackets1_13.SET_CREATIVE_MODE_SLOT, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.SHORT); // 0 - Slot\n                map(Types.ITEM1_13, Types.ITEM1_13_2); // 1 - Clicked Item\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_13_2to1_13_1/rewriter/WorldPacketRewriter1_13_2.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_13_2to1_13_1.rewriter;\n\nimport com.viaversion.viabackwards.protocol.v1_13_2to1_13_1.Protocol1_13_2To1_13_1;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.protocols.v1_12_2to1_13.packet.ClientboundPackets1_13;\n\npublic class WorldPacketRewriter1_13_2 {\n\n    public static void register(Protocol1_13_2To1_13_1 protocol) {\n        protocol.registerClientbound(ClientboundPackets1_13.LEVEL_PARTICLES, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.INT); // 0 - Particle ID\n                map(Types.BOOLEAN); // 1 - Long Distance\n                map(Types.FLOAT); // 2 - X\n                map(Types.FLOAT); // 3 - Y\n                map(Types.FLOAT); // 4 - Z\n                map(Types.FLOAT); // 5 - Offset X\n                map(Types.FLOAT); // 6 - Offset Y\n                map(Types.FLOAT); // 7 - Offset Z\n                map(Types.FLOAT); // 8 - Particle Data\n                map(Types.INT); // 9 - Particle Count\n\n                handler(wrapper -> {\n                    int id = wrapper.get(Types.INT, 0);\n                    if (id == 27) {\n                        wrapper.write(Types.ITEM1_13, wrapper.read(Types.ITEM1_13_2));\n                    }\n                });\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_13to1_12_2/Protocol1_13To1_12_2.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.viaversion.viabackwards.protocol.v1_13to1_12_2;\n\nimport com.viaversion.viabackwards.ViaBackwards;\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viabackwards.api.rewriters.text.JsonNBTComponentRewriter;\nimport com.viaversion.viabackwards.protocol.v1_13to1_12_2.data.BackwardsMappingData1_13;\nimport com.viaversion.viabackwards.protocol.v1_13to1_12_2.data.PaintingNames1_13;\nimport com.viaversion.viabackwards.protocol.v1_13to1_12_2.provider.BackwardsBlockEntityProvider;\nimport com.viaversion.viabackwards.protocol.v1_13to1_12_2.rewriter.BlockItemPacketRewriter1_13;\nimport com.viaversion.viabackwards.protocol.v1_13to1_12_2.rewriter.EntityPacketRewriter1_13;\nimport com.viaversion.viabackwards.protocol.v1_13to1_12_2.rewriter.PlayerPacketRewriter1_13;\nimport com.viaversion.viabackwards.protocol.v1_13to1_12_2.rewriter.SoundPacketRewriter1_13;\nimport com.viaversion.viabackwards.protocol.v1_13to1_12_2.storage.BackwardsBlockStorage;\nimport com.viaversion.viabackwards.protocol.v1_13to1_12_2.storage.NoteBlockStorage;\nimport com.viaversion.viabackwards.protocol.v1_13to1_12_2.storage.PlayerPositionStorage1_13;\nimport com.viaversion.viabackwards.protocol.v1_13to1_12_2.storage.TabCompleteStorage;\nimport com.viaversion.viabackwards.utils.BackwardsProtocolLogger;\nimport com.viaversion.viaversion.api.Via;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.ClientWorld;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_13;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.data.entity.EntityTrackerBase;\nimport com.viaversion.viaversion.libs.gson.JsonElement;\nimport com.viaversion.viaversion.libs.gson.JsonObject;\nimport com.viaversion.viaversion.libs.gson.JsonParser;\nimport com.viaversion.viaversion.protocols.v1_12_2to1_13.Protocol1_12_2To1_13;\nimport com.viaversion.viaversion.protocols.v1_12_2to1_13.packet.ClientboundPackets1_13;\nimport com.viaversion.viaversion.protocols.v1_12_2to1_13.packet.ServerboundPackets1_13;\nimport com.viaversion.viaversion.protocols.v1_12to1_12_1.packet.ClientboundPackets1_12_1;\nimport com.viaversion.viaversion.protocols.v1_12to1_12_1.packet.ServerboundPackets1_12_1;\nimport com.viaversion.viaversion.rewriter.text.ComponentRewriterBase;\nimport com.viaversion.viaversion.util.ComponentUtil;\nimport com.viaversion.viaversion.util.ProtocolLogger;\nimport org.checkerframework.checker.nullness.qual.Nullable;\n\npublic class Protocol1_13To1_12_2 extends BackwardsProtocol<ClientboundPackets1_13, ClientboundPackets1_12_1, ServerboundPackets1_13, ServerboundPackets1_12_1> {\n\n    public static final BackwardsMappingData1_13 MAPPINGS = new BackwardsMappingData1_13();\n    public static final ProtocolLogger LOGGER = new BackwardsProtocolLogger(Protocol1_13To1_12_2.class);\n    private final EntityPacketRewriter1_13 entityRewriter = new EntityPacketRewriter1_13(this);\n    private final BlockItemPacketRewriter1_13 blockItemPackets = new BlockItemPacketRewriter1_13(this);\n    private final JsonNBTComponentRewriter<ClientboundPackets1_13> translatableRewriter = new JsonNBTComponentRewriter<>(this, ComponentRewriterBase.ReadType.JSON) {\n        @Override\n        protected void handleTranslate(JsonObject root, String translate) {\n            String mappedKey = mappedTranslationKey(translate);\n            if (mappedKey != null || (mappedKey = getMappingData().getTranslateMappings().get(translate)) != null) {\n                root.addProperty(\"translate\", mappedKey);\n            }\n        }\n    };\n    private final JsonNBTComponentRewriter<ClientboundPackets1_13> translatableToLegacyRewriter = new JsonNBTComponentRewriter<>(this, ComponentRewriterBase.ReadType.JSON) {\n        @Override\n        protected void handleTranslate(JsonObject root, String translate) {\n            String mappedKey = mappedTranslationKey(translate);\n            if (mappedKey != null || (mappedKey = getMappingData().getTranslateMappings().get(translate)) != null) {\n                root.addProperty(\"translate\", Protocol1_12_2To1_13.MAPPINGS.getMojangTranslation().getOrDefault(mappedKey, mappedKey));\n            }\n        }\n    };\n\n    public Protocol1_13To1_12_2() {\n        super(ClientboundPackets1_13.class, ClientboundPackets1_12_1.class, ServerboundPackets1_13.class, ServerboundPackets1_12_1.class);\n    }\n\n    @Override\n    protected void registerPackets() {\n        super.registerPackets();\n\n        PaintingNames1_13.init();\n        Via.getManager().getProviders().register(BackwardsBlockEntityProvider.class, new BackwardsBlockEntityProvider());\n\n        translatableRewriter.registerLoginDisconnect();\n        translatableRewriter.registerBossEvent(ClientboundPackets1_13.BOSS_EVENT);\n        translatableRewriter.registerComponentPacket(ClientboundPackets1_13.CHAT);\n        translatableRewriter.registerLegacyOpenWindow(ClientboundPackets1_13.OPEN_SCREEN);\n        translatableRewriter.registerComponentPacket(ClientboundPackets1_13.DISCONNECT);\n        translatableRewriter.registerPlayerCombat(ClientboundPackets1_13.PLAYER_COMBAT);\n        translatableRewriter.registerTitle(ClientboundPackets1_13.SET_TITLES);\n        translatableRewriter.registerTabList(ClientboundPackets1_13.TAB_LIST);\n\n        new PlayerPacketRewriter1_13(this).register();\n        new SoundPacketRewriter1_13(this).register();\n\n        cancelClientbound(ClientboundPackets1_13.TAG_QUERY);\n        cancelClientbound(ClientboundPackets1_13.PLACE_GHOST_RECIPE);\n        cancelClientbound(ClientboundPackets1_13.RECIPE);\n        cancelClientbound(ClientboundPackets1_13.UPDATE_RECIPES);\n        cancelClientbound(ClientboundPackets1_13.UPDATE_TAGS);\n        replaceClientbound(ClientboundPackets1_13.UPDATE_ADVANCEMENTS, PacketWrapper::cancel);\n\n        cancelServerbound(ServerboundPackets1_12_1.PLACE_RECIPE);\n        cancelServerbound(ServerboundPackets1_12_1.RECIPE_BOOK_UPDATE);\n    }\n\n    @Override\n    public void init(UserConnection user) {\n        user.addEntityTracker(this.getClass(), new EntityTrackerBase(user, EntityTypes1_13.EntityType.PLAYER));\n        user.addClientWorld(this.getClass(), new ClientWorld());\n\n        user.put(new BackwardsBlockStorage());\n        user.put(new TabCompleteStorage());\n\n        if (ViaBackwards.getConfig().isFix1_13FacePlayer() && !user.has(PlayerPositionStorage1_13.class)) {\n            user.put(new PlayerPositionStorage1_13());\n        }\n\n        user.put(new NoteBlockStorage());\n    }\n\n    @Override\n    public BackwardsMappingData1_13 getMappingData() {\n        return MAPPINGS;\n    }\n\n    @Override\n    public ProtocolLogger getLogger() {\n        return LOGGER;\n    }\n\n    @Override\n    public EntityPacketRewriter1_13 getEntityRewriter() {\n        return entityRewriter;\n    }\n\n    @Override\n    public BlockItemPacketRewriter1_13 getItemRewriter() {\n        return blockItemPackets;\n    }\n\n    // Don't override the parent method\n    public JsonNBTComponentRewriter<ClientboundPackets1_13> translatableRewriter() {\n        return translatableRewriter;\n    }\n\n    public String jsonToLegacy(UserConnection connection, String value) {\n        if (value.isEmpty()) {\n            return \"\";\n        }\n\n        try {\n            return jsonToLegacy(connection, JsonParser.parseString(value));\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n        return \"\";\n    }\n\n    public String jsonToLegacy(UserConnection connection, @Nullable JsonElement value) {\n        if (value == null || value.isJsonNull()) {\n            return \"\";\n        }\n\n        translatableToLegacyRewriter.processText(connection, value);\n        return ComponentUtil.jsonToLegacy(value);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_13to1_12_2/block_entity_handlers/BannerHandler.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.viaversion.viabackwards.protocol.v1_13to1_12_2.block_entity_handlers;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.ListTag;\nimport com.viaversion.nbt.tag.NumberTag;\nimport com.viaversion.viabackwards.protocol.v1_13to1_12_2.Protocol1_13To1_12_2;\nimport com.viaversion.viabackwards.protocol.v1_13to1_12_2.provider.BackwardsBlockEntityProvider.BackwardsBlockEntityHandler;\n\npublic class BannerHandler implements BackwardsBlockEntityHandler {\n    private static final int WALL_BANNER_START = 7110; // 4 each\n    private static final int WALL_BANNER_STOP = 7173;\n\n    private static final int BANNER_START = 6854; // 16 each\n    private static final int BANNER_STOP = 7109;\n\n    @Override\n    public CompoundTag transform(int blockId, CompoundTag tag) {\n        // Normal banners\n        if (blockId >= BANNER_START && blockId <= BANNER_STOP) {\n            int color = (blockId - BANNER_START) >> 4;\n            tag.putInt(\"Base\", 15 - color);\n        }\n        // Wall banners\n        else if (blockId >= WALL_BANNER_START && blockId <= WALL_BANNER_STOP) {\n            int color = (blockId - WALL_BANNER_START) >> 2;\n            tag.putInt(\"Base\", 15 - color);\n        } else {\n            Protocol1_13To1_12_2.LOGGER.warning(\"Why does this block have the banner block entity? :(\" + tag);\n        }\n\n        // Invert colors\n        ListTag<CompoundTag> patternsTag = tag.getListTag(\"Patterns\", CompoundTag.class);\n        if (patternsTag != null) {\n            for (CompoundTag pattern : patternsTag) {\n                NumberTag colorTag = pattern.getNumberTag(\"Color\");\n                if (colorTag != null) {\n                    pattern.putInt(\"Color\", 15 - colorTag.asInt()); // Invert color id\n                }\n            }\n        }\n\n        return tag;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_13to1_12_2/block_entity_handlers/BedHandler.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.viaversion.viabackwards.protocol.v1_13to1_12_2.block_entity_handlers;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.viabackwards.protocol.v1_13to1_12_2.provider.BackwardsBlockEntityProvider;\n\npublic class BedHandler implements BackwardsBlockEntityProvider.BackwardsBlockEntityHandler {\n\n    @Override\n    public CompoundTag transform(int blockId, CompoundTag tag) {\n        int offset = blockId - 748;\n        int color = offset >> 4;\n\n        tag.putInt(\"color\", color);\n\n        return tag;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_13to1_12_2/block_entity_handlers/FlowerPotHandler.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.viaversion.viabackwards.protocol.v1_13to1_12_2.block_entity_handlers;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.viabackwards.protocol.v1_13to1_12_2.provider.BackwardsBlockEntityProvider;\nimport com.viaversion.viaversion.libs.fastutil.ints.Int2ObjectMap;\nimport com.viaversion.viaversion.libs.fastutil.ints.Int2ObjectOpenHashMap;\nimport com.viaversion.viaversion.util.Pair;\n\npublic class FlowerPotHandler implements BackwardsBlockEntityProvider.BackwardsBlockEntityHandler {\n\n    private static final Int2ObjectMap<Pair<String, Byte>> FLOWERS = new Int2ObjectOpenHashMap<>(22, 0.99F);\n    private static final Pair<String, Byte> AIR = new Pair<>(\"minecraft:air\", (byte) 0);\n\n    static {\n        FLOWERS.put(5265, AIR);\n        register(5266, \"minecraft:sapling\", (byte) 0);\n        register(5267, \"minecraft:sapling\", (byte) 1);\n        register(5268, \"minecraft:sapling\", (byte) 2);\n        register(5269, \"minecraft:sapling\", (byte) 3);\n        register(5270, \"minecraft:sapling\", (byte) 4);\n        register(5271, \"minecraft:sapling\", (byte) 5);\n        register(5272, \"minecraft:tallgrass\", (byte) 2);\n        register(5273, \"minecraft:yellow_flower\", (byte) 0);\n        register(5274, \"minecraft:red_flower\", (byte) 0);\n        register(5275, \"minecraft:red_flower\", (byte) 1);\n        register(5276, \"minecraft:red_flower\", (byte) 2);\n        register(5277, \"minecraft:red_flower\", (byte) 3);\n        register(5278, \"minecraft:red_flower\", (byte) 4);\n        register(5279, \"minecraft:red_flower\", (byte) 5);\n        register(5280, \"minecraft:red_flower\", (byte) 6);\n        register(5281, \"minecraft:red_flower\", (byte) 7);\n        register(5282, \"minecraft:red_flower\", (byte) 8);\n        register(5283, \"minecraft:red_mushroom\", (byte) 0);\n        register(5284, \"minecraft:brown_mushroom\", (byte) 0);\n        register(5285, \"minecraft:deadbush\", (byte) 0);\n        register(5286, \"minecraft:cactus\", (byte) 0);\n    }\n\n    private static void register(int id, String identifier, byte data) {\n        FLOWERS.put(id, new Pair<>(identifier, data));\n    }\n\n    public static boolean isFlowah(int id) {\n        return id >= 5265 && id <= 5286;\n    }\n\n    public Pair<String, Byte> getOrDefault(int blockId) {\n        Pair<String, Byte> pair = FLOWERS.get(blockId);\n        return pair != null ? pair : AIR;\n    }\n\n    // TODO THIS IS NEVER CALLED BECAUSE ITS NO LONGER A BLOCK ENTITY :(\n    @Override\n    public CompoundTag transform(int blockId, CompoundTag tag) {\n        Pair<String, Byte> item = getOrDefault(blockId);\n\n        tag.putString(\"Item\", item.key());\n        tag.putInt(\"Data\", item.value());\n\n        return tag;\n    }\n\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_13to1_12_2/block_entity_handlers/PistonHandler.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_13to1_12_2.block_entity_handlers;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.ListTag;\nimport com.viaversion.nbt.tag.StringTag;\nimport com.viaversion.nbt.tag.Tag;\nimport com.viaversion.viabackwards.protocol.v1_13to1_12_2.Protocol1_13To1_12_2;\nimport com.viaversion.viabackwards.protocol.v1_13to1_12_2.provider.BackwardsBlockEntityProvider;\nimport com.viaversion.viaversion.api.Via;\nimport com.viaversion.viaversion.api.data.MappingDataLoader;\nimport com.viaversion.viaversion.libs.fastutil.objects.Object2IntMap;\nimport com.viaversion.viaversion.libs.fastutil.objects.Object2IntOpenHashMap;\nimport com.viaversion.viaversion.protocols.v1_12_2to1_13.blockconnections.ConnectionData;\nimport java.util.Map;\nimport java.util.StringJoiner;\n\npublic class PistonHandler implements BackwardsBlockEntityProvider.BackwardsBlockEntityHandler {\n\n    private final Object2IntMap<String> pistonIds = new Object2IntOpenHashMap<>();\n\n    public PistonHandler() {\n        pistonIds.defaultReturnValue(-1);\n        if (Via.getConfig().isServersideBlockConnections()) {\n            Map<String, Integer> keyToId = ConnectionData.getKeyToId();\n            for (Map.Entry<String, Integer> entry : keyToId.entrySet()) {\n                if (!entry.getKey().contains(\"piston\")) {\n                    continue;\n                }\n\n                addEntries(entry.getKey(), entry.getValue());\n            }\n        } else {\n            ListTag<StringTag> blockStates = MappingDataLoader.INSTANCE.loadNBT(\"blockstates-1.13.nbt\").getListTag(\"blockstates\", StringTag.class);\n            for (int id = 0; id < blockStates.size(); id++) {\n                StringTag state = blockStates.get(id);\n                String key = state.getValue();\n                if (!key.contains(\"piston\")) {\n                    continue;\n                }\n\n                addEntries(key, id);\n            }\n        }\n    }\n\n    // There doesn't seem to be a nicer way around it :(\n    private void addEntries(String data, int id) {\n        id = Protocol1_13To1_12_2.MAPPINGS.getNewBlockStateId(id);\n        pistonIds.put(data, id);\n\n        String substring = data.substring(10);\n        if (!substring.startsWith(\"piston\") && !substring.startsWith(\"sticky_piston\")) return;\n\n        // Swap properties and add them to the map\n        String[] split = data.substring(0, data.length() - 1).split(\"\\\\[\");\n        String[] properties = split[1].split(\",\");\n        data = split[0] + \"[\" + properties[1] + \",\" + properties[0] + \"]\";\n        pistonIds.put(data, id);\n    }\n\n    @Override\n    public CompoundTag transform(int blockId, CompoundTag tag) {\n        CompoundTag blockState = tag.getCompoundTag(\"blockState\");\n        if (blockState == null) return tag;\n\n        String dataFromTag = getDataFromTag(blockState);\n        if (dataFromTag == null) return tag;\n\n        int id = pistonIds.getInt(dataFromTag);\n        if (id == -1) {\n            //TODO see why this could be null and if this is bad\n            return tag;\n        }\n\n        tag.putInt(\"blockId\", id >> 4);\n        tag.putInt(\"blockData\", id & 15);\n        return tag;\n    }\n\n    // The type hasn't actually been updated in the blockstorage, so we need to construct it\n    private String getDataFromTag(CompoundTag tag) {\n        StringTag name = tag.getStringTag(\"Name\");\n        if (name == null) return null;\n\n        CompoundTag properties = tag.getCompoundTag(\"Properties\");\n        if (properties == null) return name.getValue();\n\n        StringJoiner joiner = new StringJoiner(\",\", name.getValue() + \"[\", \"]\");\n        for (Map.Entry<String, Tag> entry : properties) {\n            if (!(entry.getValue() instanceof StringTag)) continue;\n            joiner.add(entry.getKey() + \"=\" + ((StringTag) entry.getValue()).getValue());\n        }\n        return joiner.toString();\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_13to1_12_2/block_entity_handlers/SkullHandler.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.viaversion.viabackwards.protocol.v1_13to1_12_2.block_entity_handlers;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.viabackwards.protocol.v1_13to1_12_2.provider.BackwardsBlockEntityProvider.BackwardsBlockEntityHandler;\n\npublic class SkullHandler implements BackwardsBlockEntityHandler {\n    private static final int SKULL_START = 5447;\n\n    @Override\n    public CompoundTag transform(int blockId, CompoundTag tag) {\n        int diff = blockId - SKULL_START;\n        int pos = diff % 20;\n        byte type = (byte) Math.floor(diff / 20f);\n\n        // Set type\n        tag.putByte(\"SkullType\", type);\n\n        // Remove wall skulls\n        if (pos < 4) {\n            return tag;\n        }\n\n        // Add rotation for normal skulls\n        tag.putByte(\"Rot\", (byte) ((pos - 4) & 255));\n\n        return tag;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_13to1_12_2/block_entity_handlers/SpawnerHandler.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.viaversion.viabackwards.protocol.v1_13to1_12_2.block_entity_handlers;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.StringTag;\nimport com.viaversion.viabackwards.protocol.v1_13to1_12_2.data.EntityNameMappings1_12_2;\nimport com.viaversion.viabackwards.protocol.v1_13to1_12_2.provider.BackwardsBlockEntityProvider;\n\npublic class SpawnerHandler implements BackwardsBlockEntityProvider.BackwardsBlockEntityHandler {\n\n    @Override\n    public CompoundTag transform(int blockId, CompoundTag tag) {\n        CompoundTag dataTag = tag.getCompoundTag(\"SpawnData\");\n        if (dataTag != null) {\n            StringTag idTag = dataTag.getStringTag(\"id\");\n            if (idTag != null) {\n                idTag.setValue(EntityNameMappings1_12_2.rewrite(idTag.getValue()));\n            }\n        }\n        return tag;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_13to1_12_2/data/BackwardsMappingData1_13.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.viaversion.viabackwards.protocol.v1_13to1_12_2.data;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.viabackwards.api.data.BackwardsMappingData;\nimport com.viaversion.viaversion.libs.fastutil.ints.Int2ObjectMap;\nimport com.viaversion.viaversion.libs.fastutil.ints.Int2ObjectOpenHashMap;\nimport com.viaversion.viaversion.protocols.v1_12_2to1_13.Protocol1_12_2To1_13;\nimport com.viaversion.viaversion.protocols.v1_12_2to1_13.data.StatisticMappings1_13;\nimport java.util.HashMap;\nimport java.util.Map;\n\npublic class BackwardsMappingData1_13 extends BackwardsMappingData {\n    private final Int2ObjectMap<String> statisticMappings = new Int2ObjectOpenHashMap<>();\n    private final Map<String, String> translateMappings = new HashMap<>();\n\n    public BackwardsMappingData1_13() {\n        super(\"1.13\", \"1.12\", Protocol1_12_2To1_13.class);\n    }\n\n    @Override\n    public void loadExtras(final CompoundTag data) {\n        super.loadExtras(data);\n\n        for (Map.Entry<String, Integer> entry : StatisticMappings1_13.CUSTOM_STATS.entrySet()) {\n            statisticMappings.put(entry.getValue().intValue(), entry.getKey());\n        }\n        for (Map.Entry<String, String> entry : Protocol1_12_2To1_13.MAPPINGS.getTranslateMapping().entrySet()) {\n            translateMappings.put(entry.getValue(), entry.getKey());\n        }\n    }\n\n    @Override\n    public int getNewBlockStateId(int id) {\n        // Comparator funkyness: https://github.com/ViaVersion/ViaBackwards/issues/524\n        if (id >= 5635 && id <= 5650) {\n            if (id < 5639) {\n                id += 4;\n            } else if (id < 5643) {\n                id -= 4;\n            } else if (id < 5647) {\n                id += 4;\n            } else {\n                id -= 4;\n            }\n        }\n\n        int mappedId = super.getNewBlockStateId(id);\n\n        // https://github.com/ViaVersion/ViaBackwards/issues/290\n        return switch (mappedId) {\n            case 1595, 1596, 1597 -> 1584; // brown mushroom block\n            case 1611, 1612, 1613 -> 1600; // red mushroom block\n            default -> mappedId;\n        };\n    }\n\n    @Override\n    protected int checkValidity(int id, int mappedId, String type) {\n        // Don't warn for missing ids here\n        return mappedId;\n    }\n\n    public Int2ObjectMap<String> getStatisticMappings() {\n        return statisticMappings;\n    }\n\n    public Map<String, String> getTranslateMappings() {\n        return translateMappings;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_13to1_12_2/data/EntityIdMappings1_12_2.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_13to1_12_2.data;\n\nimport com.viaversion.viaversion.libs.fastutil.ints.Int2IntMap;\nimport com.viaversion.viaversion.libs.fastutil.ints.Int2IntOpenHashMap;\nimport com.viaversion.viaversion.protocols.v1_12_2to1_13.data.EntityIdMappings1_13;\n\npublic class EntityIdMappings1_12_2 {\n    private static final Int2IntMap TYPES = new Int2IntOpenHashMap();\n\n    static {\n        TYPES.defaultReturnValue(-1);\n        for (Int2IntMap.Entry entry : EntityIdMappings1_13.getEntityTypes().int2IntEntrySet()) {\n            EntityIdMappings1_12_2.TYPES.put(entry.getIntValue(), entry.getIntKey());\n        }\n    }\n\n    public static int getOldId(int type1_13) {\n        return TYPES.get(type1_13);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_13to1_12_2/data/EntityNameMappings1_12_2.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.viaversion.viabackwards.protocol.v1_13to1_12_2.data;\n\nimport com.viaversion.viaversion.util.Key;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Objects;\n\npublic class EntityNameMappings1_12_2 {\n    private static final Map<String, String> ENTITY_NAMES = new HashMap<>();\n\n    static {\n        // CHANGED NAMES IN 1.13\n        reg(\"commandblock_minecart\", \"command_block_minecart\");\n        reg(\"ender_crystal\", \"end_crystal\");\n        reg(\"evocation_fangs\", \"evoker_fangs\");\n        reg(\"evocation_illager\", \"evoker\");\n        reg(\"eye_of_ender_signal\", \"eye_of_ender\");\n        reg(\"fireworks_rocket\", \"firework_rocket\");\n        reg(\"illusion_illager\", \"illusioner\");\n        reg(\"snowman\", \"snow_golem\");\n        reg(\"villager_golem\", \"iron_golem\");\n        reg(\"vindication_illager\", \"vindicator\");\n        reg(\"xp_bottle\", \"experience_bottle\");\n        reg(\"xp_orb\", \"experience_orb\");\n    }\n\n\n    private static void reg(String past, String future) {\n        ENTITY_NAMES.put(Key.namespaced(future), Key.namespaced(past));\n    }\n\n    public static String rewrite(String entName) {\n        String entityName = ENTITY_NAMES.get(Key.namespaced(entName));\n        return Objects.requireNonNullElse(entityName, entName);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_13to1_12_2/data/NamedSoundMappings1_12_2.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_13to1_12_2.data;\n\nimport com.viaversion.viaversion.protocols.v1_12_2to1_13.data.NamedSoundMappings1_13;\nimport com.viaversion.viaversion.util.Key;\nimport java.lang.reflect.Field;\nimport java.util.HashMap;\nimport java.util.Map;\n\npublic class NamedSoundMappings1_12_2 {\n    private static final Map<String, String> SOUNDS = new HashMap<>();\n\n    static {\n        try {\n            Field field = NamedSoundMappings1_13.class.getDeclaredField(\"oldToNew\");\n            field.setAccessible(true);\n            Map<String, String> sounds = (Map<String, String>) field.get(null);\n            sounds.forEach((sound1_12, sound1_13) -> NamedSoundMappings1_12_2.SOUNDS.put(sound1_13, sound1_12));\n        } catch (NoSuchFieldException | IllegalAccessException ex) {\n            ex.printStackTrace();\n        }\n    }\n\n    public static String getOldId(String sound1_13) {\n        return SOUNDS.get(Key.stripMinecraftNamespace(sound1_13));\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_13to1_12_2/data/PaintingNames1_13.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_13to1_12_2.data;\n\nimport com.viaversion.viaversion.libs.fastutil.ints.Int2ObjectMap;\nimport com.viaversion.viaversion.libs.fastutil.ints.Int2ObjectOpenHashMap;\n\npublic class PaintingNames1_13 {\n    private static final Int2ObjectMap<String> PAINTINGS = new Int2ObjectOpenHashMap<>(26, 0.99F);\n\n    public static void init() {\n        add(\"Kebab\");\n        add(\"Aztec\");\n        add(\"Alban\");\n        add(\"Aztec2\");\n        add(\"Bomb\");\n        add(\"Plant\");\n        add(\"Wasteland\");\n        add(\"Pool\");\n        add(\"Courbet\");\n        add(\"Sea\");\n        add(\"Sunset\");\n        add(\"Creebet\");\n        add(\"Wanderer\");\n        add(\"Graham\");\n        add(\"Match\");\n        add(\"Bust\");\n        add(\"Stage\");\n        add(\"Void\");\n        add(\"SkullAndRoses\");\n        add(\"Wither\");\n        add(\"Fighters\");\n        add(\"Pointer\");\n        add(\"Pigscene\");\n        add(\"BurningSkull\");\n        add(\"Skeleton\");\n        add(\"DonkeyKong\");\n    }\n\n    private static void add(String motive) {\n        PAINTINGS.put(PAINTINGS.size(), motive);\n    }\n\n    public static String getStringId(int id) {\n        return PAINTINGS.getOrDefault(id, \"kebab\");\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_13to1_12_2/data/ParticleIdMappings1_12_2.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.viaversion.viabackwards.protocol.v1_13to1_12_2.data;\n\nimport com.viaversion.viabackwards.protocol.v1_13to1_12_2.Protocol1_13To1_12_2;\nimport com.viaversion.viaversion.api.minecraft.Particle;\nimport com.viaversion.viaversion.api.minecraft.item.Item;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.type.Types;\nimport java.util.List;\nimport org.checkerframework.checker.nullness.qual.Nullable;\n\npublic class ParticleIdMappings1_12_2 {\n    private static final ParticleData[] particles;\n\n    static {\n        ParticleHandler blockHandler = new ParticleHandler() {\n            @Override\n            public int[] rewrite(Protocol1_13To1_12_2 protocol, PacketWrapper wrapper) {\n                return rewrite(wrapper.read(Types.VAR_INT));\n            }\n\n            @Override\n            public int[] rewrite(Protocol1_13To1_12_2 protocol, List<Particle.ParticleData<?>> data) {\n                return rewrite((int) data.get(0).getValue());\n            }\n\n            private int[] rewrite(int newType) {\n                int blockType = Protocol1_13To1_12_2.MAPPINGS.getNewBlockStateId(newType);\n\n                int type = blockType >> 4;\n                int meta = blockType & 15;\n                return new int[]{type + (meta << 12)};\n            }\n\n            @Override\n            public boolean isBlockHandler() {\n                return true;\n            }\n        };\n\n        particles = new ParticleData[]{\n            rewrite(16), // (0->16)  minecraft:ambient_entity_effect -> mobSpellAmbient\n            rewrite(20), // (1->20)  minecraft:angry_villager -> angryVillager\n            rewrite(35), // (2->35)  minecraft:barrier -> barrier\n            rewrite(37, blockHandler),\n            // (3->37)  minecraft:block -> blockcrack\n            rewrite(4),  // (4->4)   minecraft:bubble -> bubble\n            rewrite(29), // (5->29)  minecraft:cloud -> cloud\n            rewrite(9),  // (6->9)   minecraft:crit -> crit\n            rewrite(44), // (7->44)  minecraft:damage_indicator -> damageIndicator\n            rewrite(42), // (8->42)  minecraft:dragon_breath -> dragonbreath\n            rewrite(19), // (9->19)  minecraft:dripping_lava -> dripLava\n            rewrite(18), // (10->18) minecraft:dripping_water -> dripWater\n            rewrite(30, new ParticleHandler() {\n                @Override\n                public int[] rewrite(Protocol1_13To1_12_2 protocol, PacketWrapper wrapper) {\n                    float r = wrapper.read(Types.FLOAT);\n                    float g = wrapper.read(Types.FLOAT);\n                    float b = wrapper.read(Types.FLOAT);\n                    float scale = wrapper.read(Types.FLOAT);\n\n                    wrapper.set(Types.FLOAT, 3, r); // 5 - Offset X index=3\n                    wrapper.set(Types.FLOAT, 4, g); // 6 - Offset Y index=4\n                    wrapper.set(Types.FLOAT, 5, b); // 7 - Offset Z index=5\n                    wrapper.set(Types.FLOAT, 6, scale); // 8 - Particle Data index=6\n                    wrapper.set(Types.INT, 1, 0); // 9 - Particle Count index=1 enable rgb particle\n\n                    return null;\n                }\n\n                @Override\n                public int[] rewrite(Protocol1_13To1_12_2 protocol, List<Particle.ParticleData<?>> data) {\n                    return null;\n                }\n            }),         // (11->30) minecraft:dust -> reddust\n            rewrite(13), // (12->13) minecraft:effect -> spell\n            rewrite(41), // (13->41) minecraft:elder_guardian -> mobappearance\n            rewrite(10), // (14->10) minecraft:enchanted_hit -> magicCrit‌\n            rewrite(25), // (15->25) minecraft:enchant -> enchantmenttable\n            rewrite(43), // (16->43) minecraft:end_rod -> endRod\n            rewrite(15), // (17->15) minecraft:entity_effect -> mobSpell\n            rewrite(2),  // (18->2)  minecraft:explosion_emitter -> hugeexplosion\n            rewrite(1),  // (19->1)  minecraft:explosion -> largeexplode\n            rewrite(46, blockHandler),\n            // (20->46) minecraft:falling_dust -> fallingdust\n            rewrite(3),  // (21->3)  minecraft:firework -> fireworksSpark\n            rewrite(6),  // (22->6)  minecraft:fishing -> wake\n            rewrite(26), // (23->26) minecraft:flame -> flame\n            rewrite(21), // (24->21) minecraft:happy_villager -> happyVillager\n            rewrite(34), // (25->34) minecraft:heart -> heart\n            rewrite(14), // (26->14) minecraft:instant_effect -> instantSpell\n            rewrite(36, new ParticleHandler() {\n                @Override\n                public int[] rewrite(Protocol1_13To1_12_2 protocol, PacketWrapper wrapper) {\n                    return rewrite(protocol, wrapper.read(Types.ITEM1_13));\n                }\n\n                @Override\n                public int[] rewrite(Protocol1_13To1_12_2 protocol, List<Particle.ParticleData<?>> data) {\n                    return rewrite(protocol, (Item) data.get(0).getValue());\n                }\n\n                private int[] rewrite(Protocol1_13To1_12_2 protocol, Item newItem) {\n                    Item item = protocol.getItemRewriter().handleItemToClient(null, newItem);\n                    return new int[]{item.identifier(), item.data()};\n                }\n            }),          // (27->36) minecraft:item -> iconcrack\n            rewrite(33), // (28->33) minecraft:item_slime -> slime\n            rewrite(31), // (29->31) minecraft:item_snowball -> snowballpoof\n            rewrite(12), // (30->12) minecraft:large_smoke -> largesmoke\n            rewrite(27), // (31->27) minecraft:lava -> lava\n            rewrite(22), // (32->22) minecraft:mycelium -> townaura\n            rewrite(23), // (33->23) minecraft:note -> note\n            rewrite(0),  // (34->0)  minecraft:poof -> explode\n            rewrite(24), // (35->24) minecraft:portal -> portal\n            rewrite(39), // (36->39) minecraft:rain -> droplet\n            rewrite(11), // (37->11) minecraft:smoke -> smoke\n            rewrite(48), // (38->48) minecraft:spit -> spit\n            rewrite(12), // (39->-1) minecraft:squid_ink -> squid_ink -> large_smoke\n            rewrite(45), // (40->45) minecraft:sweep_attack -> sweepAttack‌\n            rewrite(47), // (41->47) minecraft:totem_of_undying -> totem\n            rewrite(7),  // (42->7)  minecraft:underwater -> suspended‌\n            rewrite(5),  // (43->5)  minecraft:splash -> splash\n            rewrite(17), // (44->17) minecraft:witch -> witchMagic\n            rewrite(4),  // (45->4)  minecraft:bubble_pop -> bubble\n            rewrite(4),  // (46->4)  minecraft:current_down -> bubble\n            rewrite(4),  // (47->4)  minecraft:bubble_column_up -> bubble\n            rewrite(18), // (48->-1) minecraft:nautilus -> nautilus -> dripWater\n            rewrite(18), // (49->18) minecraft:dolphin -> dripWater\n        };\n    }\n\n    public static ParticleData getMapping(int id) {\n        return particles[id];\n    }\n\n    private static ParticleData rewrite(int replacementId) {\n        return new ParticleData(replacementId);\n    }\n\n    private static ParticleData rewrite(int replacementId, ParticleHandler handler) {\n        return new ParticleData(replacementId, handler);\n    }\n\n    public interface ParticleHandler {\n\n        int[] rewrite(Protocol1_13To1_12_2 protocol, PacketWrapper wrapper);\n\n        int[] rewrite(Protocol1_13To1_12_2 protocol, List<Particle.ParticleData<?>> data);\n\n        default boolean isBlockHandler() {\n            return false;\n        }\n    }\n\n    public static final class ParticleData {\n        private final int historyId;\n        private final ParticleHandler handler;\n\n        private ParticleData(int historyId, ParticleHandler handler) {\n            this.historyId = historyId;\n            this.handler = handler;\n        }\n\n        private ParticleData(int historyId) {\n            this(historyId, null);\n        }\n\n        public int @Nullable [] rewriteData(Protocol1_13To1_12_2 protocol, PacketWrapper wrapper) {\n            if (handler == null) return null;\n            return handler.rewrite(protocol, wrapper);\n        }\n\n        public int @Nullable [] rewriteMeta(Protocol1_13To1_12_2 protocol, List<Particle.ParticleData<?>> data) {\n            if (handler == null) return null;\n            return handler.rewrite(protocol, data);\n        }\n\n        public int getHistoryId() {\n            return historyId;\n        }\n\n        public ParticleHandler getHandler() {\n            return handler;\n        }\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_13to1_12_2/provider/BackwardsBlockEntityProvider.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.viaversion.viabackwards.protocol.v1_13to1_12_2.provider;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.StringTag;\nimport com.viaversion.viabackwards.protocol.v1_13to1_12_2.block_entity_handlers.BannerHandler;\nimport com.viaversion.viabackwards.protocol.v1_13to1_12_2.block_entity_handlers.BedHandler;\nimport com.viaversion.viabackwards.protocol.v1_13to1_12_2.block_entity_handlers.FlowerPotHandler;\nimport com.viaversion.viabackwards.protocol.v1_13to1_12_2.block_entity_handlers.PistonHandler;\nimport com.viaversion.viabackwards.protocol.v1_13to1_12_2.block_entity_handlers.SkullHandler;\nimport com.viaversion.viabackwards.protocol.v1_13to1_12_2.block_entity_handlers.SpawnerHandler;\nimport com.viaversion.viabackwards.protocol.v1_13to1_12_2.storage.BackwardsBlockStorage;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.BlockPosition;\nimport com.viaversion.viaversion.api.platform.providers.Provider;\nimport com.viaversion.viaversion.util.Key;\nimport java.util.HashMap;\nimport java.util.Map;\n\npublic class BackwardsBlockEntityProvider implements Provider {\n    private final Map<String, BackwardsBlockEntityProvider.BackwardsBlockEntityHandler> handlers = new HashMap<>();\n\n    public BackwardsBlockEntityProvider() {\n        handlers.put(\"flower_pot\", new FlowerPotHandler()); // TODO requires special treatment, manually send\n        handlers.put(\"bed\", new BedHandler());\n        handlers.put(\"banner\", new BannerHandler());\n        handlers.put(\"skull\", new SkullHandler());\n        handlers.put(\"mob_spawner\", new SpawnerHandler());\n        handlers.put(\"piston\", new PistonHandler());\n    }\n\n    /**\n     * Check if a block entity handler is present\n     *\n     * @param key Id of the NBT data ex: minecraft:bed\n     * @return true if present\n     */\n    public boolean isHandled(String key) {\n        return handlers.containsKey(Key.stripMinecraftNamespace(key));\n    }\n\n    /**\n     * Transform blocks to block entities!\n     *\n     * @param user     The user\n     * @param position The position of the block entity\n     * @param tag      The block entity tag\n     */\n    public CompoundTag transform(UserConnection user, BlockPosition position, CompoundTag tag) {\n        final StringTag idTag = tag.getStringTag(\"id\");\n        if (idTag == null) {\n            return tag;\n        }\n\n        String id = idTag.getValue();\n        BackwardsBlockEntityHandler handler = handlers.get(Key.stripMinecraftNamespace(id));\n        if (handler == null) {\n            return tag;\n        }\n\n        BackwardsBlockStorage storage = user.get(BackwardsBlockStorage.class);\n        Integer blockId = storage.get(position);\n        if (blockId == null) {\n            return tag;\n        }\n\n        return handler.transform(blockId, tag);\n    }\n\n    /**\n     * Transform blocks to block entities!\n     *\n     * @param user     The user\n     * @param position The position of the block entity\n     * @param id       The block entity id\n     */\n    public CompoundTag transform(UserConnection user, BlockPosition position, String id) {\n        CompoundTag tag = new CompoundTag();\n        tag.putString(\"id\", id);\n        tag.putInt(\"x\", Math.toIntExact(position.x()));\n        tag.putInt(\"y\", Math.toIntExact(position.y()));\n        tag.putInt(\"z\", Math.toIntExact(position.z()));\n\n        return this.transform(user, position, tag);\n    }\n\n    @FunctionalInterface\n    public interface BackwardsBlockEntityHandler {\n\n        CompoundTag transform(int blockId, CompoundTag tag);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_13to1_12_2/rewriter/BlockItemPacketRewriter1_13.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.viaversion.viabackwards.protocol.v1_13to1_12_2.rewriter;\n\nimport com.google.common.primitives.Ints;\nimport com.viaversion.nbt.tag.ByteTag;\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.IntTag;\nimport com.viaversion.nbt.tag.ListTag;\nimport com.viaversion.nbt.tag.NumberTag;\nimport com.viaversion.nbt.tag.StringTag;\nimport com.viaversion.nbt.tag.Tag;\nimport com.viaversion.viabackwards.ViaBackwards;\nimport com.viaversion.viabackwards.api.rewriters.BackwardsItemRewriter;\nimport com.viaversion.viabackwards.api.rewriters.EnchantmentRewriter;\nimport com.viaversion.viabackwards.protocol.v1_13to1_12_2.Protocol1_13To1_12_2;\nimport com.viaversion.viabackwards.protocol.v1_13to1_12_2.block_entity_handlers.FlowerPotHandler;\nimport com.viaversion.viabackwards.protocol.v1_13to1_12_2.provider.BackwardsBlockEntityProvider;\nimport com.viaversion.viabackwards.protocol.v1_13to1_12_2.storage.BackwardsBlockStorage;\nimport com.viaversion.viabackwards.protocol.v1_13to1_12_2.storage.NoteBlockStorage;\nimport com.viaversion.viaversion.api.Via;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.BlockChangeRecord;\nimport com.viaversion.viaversion.api.minecraft.BlockPosition;\nimport com.viaversion.viaversion.api.minecraft.ClientWorld;\nimport com.viaversion.viaversion.api.minecraft.chunks.Chunk;\nimport com.viaversion.viaversion.api.minecraft.chunks.ChunkSection;\nimport com.viaversion.viaversion.api.minecraft.chunks.DataPalette;\nimport com.viaversion.viaversion.api.minecraft.chunks.PaletteType;\nimport com.viaversion.viaversion.api.minecraft.item.Item;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.chunk.ChunkType1_13;\nimport com.viaversion.viaversion.api.type.types.chunk.ChunkType1_9_3;\nimport com.viaversion.viaversion.protocols.v1_12_2to1_13.Protocol1_12_2To1_13;\nimport com.viaversion.viaversion.protocols.v1_12_2to1_13.data.BlockIdData;\nimport com.viaversion.viaversion.protocols.v1_12_2to1_13.data.SpawnEggMappings1_13;\nimport com.viaversion.viaversion.protocols.v1_12_2to1_13.packet.ClientboundPackets1_13;\nimport com.viaversion.viaversion.protocols.v1_12to1_12_1.packet.ClientboundPackets1_12_1;\nimport com.viaversion.viaversion.protocols.v1_12to1_12_1.packet.ServerboundPackets1_12_1;\nimport com.viaversion.viaversion.util.ComponentUtil;\nimport com.viaversion.viaversion.util.IdAndData;\nimport com.viaversion.viaversion.util.Key;\nimport com.viaversion.viaversion.util.Pair;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.Optional;\n\npublic class BlockItemPacketRewriter1_13 extends BackwardsItemRewriter<ClientboundPackets1_13, ServerboundPackets1_12_1, Protocol1_13To1_12_2> {\n\n    private final Map<String, String> enchantmentMappings = new HashMap<>();\n    private final String extraNbtTag;\n\n    public BlockItemPacketRewriter1_13(Protocol1_13To1_12_2 protocol) {\n        super(protocol, Types.ITEM1_13, Types.ITEM1_13_SHORT_ARRAY, Types.ITEM1_8, Types.ITEM1_8_SHORT_ARRAY);\n        extraNbtTag = nbtTagName(\"2\");\n    }\n\n    public static boolean isDamageable(int id) {\n        return id >= 256 && id <= 259 // iron shovel, pickaxe, axe, flint and steel\n            || id == 261 // bow\n            || id >= 267 && id <= 279 // iron sword, wooden+stone+diamond swords, shovels, pickaxes, axes\n            || id >= 283 && id <= 286 // gold sword, shovel, pickaxe, axe\n            || id >= 290 && id <= 294 // hoes\n            || id >= 298 && id <= 317 // armors\n            || id == 346 // fishing rod\n            || id == 359 // shears\n            || id == 398 // carrot on a stick\n            || id == 442 // shield\n            || id == 443; // elytra\n    }\n\n    @Override\n    protected void registerPackets() {\n        protocol.replaceClientbound(ClientboundPackets1_13.COOLDOWN, wrapper -> {\n            int itemId = wrapper.read(Types.VAR_INT);\n            int oldId = protocol.getMappingData().getItemMappings().getNewId(itemId);\n            if (oldId == -1) {\n                wrapper.cancel();\n                return;\n            }\n\n            if (SpawnEggMappings1_13.getEntityId(oldId).isPresent()) {\n                wrapper.write(Types.VAR_INT, IdAndData.toRawData(383));\n                return;\n            }\n\n            wrapper.write(Types.VAR_INT, IdAndData.getId(oldId));\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_13.BLOCK_EVENT, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.BLOCK_POSITION1_8); // Location\n                map(Types.UNSIGNED_BYTE); // Action Id\n                map(Types.UNSIGNED_BYTE); // Action param\n                map(Types.VAR_INT); // Block Id - /!\\ NOT BLOCK STATE ID\n                handler(wrapper -> {\n                    int blockId = wrapper.get(Types.VAR_INT, 0);\n\n                    if (blockId == 73)\n                        blockId = 25;\n                    else if (blockId == 99)\n                        blockId = 33;\n                    else if (blockId == 92)\n                        blockId = 29;\n                    else if (blockId == 142)\n                        blockId = 54;\n                    else if (blockId == 305)\n                        blockId = 146;\n                    else if (blockId == 249)\n                        blockId = 130;\n                    else if (blockId == 257)\n                        blockId = 138;\n                    else if (blockId == 140)\n                        blockId = 52;\n                    else if (blockId == 472)\n                        blockId = 209;\n                    else if (blockId >= 483 && blockId <= 498)\n                        blockId = blockId - 483 + 219;\n\n                    if (blockId == 25) { // Note block\n                        final NoteBlockStorage noteBlockStorage = wrapper.user().get(NoteBlockStorage.class);\n\n                        final BlockPosition position = wrapper.get(Types.BLOCK_POSITION1_8, 0);\n                        final Pair<Integer, Integer> update = noteBlockStorage.getNoteBlockUpdate(position);\n                        if (update != null) { // Use values from block state update\n                            wrapper.set(Types.UNSIGNED_BYTE, 0, update.key().shortValue());\n                            wrapper.set(Types.UNSIGNED_BYTE, 1, update.value().shortValue());\n                        }\n                    }\n\n                    wrapper.set(Types.VAR_INT, 0, blockId);\n                });\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_13.BLOCK_ENTITY_DATA, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.BLOCK_POSITION1_8); // 0 - Position\n                map(Types.UNSIGNED_BYTE); // 1 - Action\n                map(Types.NAMED_COMPOUND_TAG); // 2 - NBT Data\n\n                handler(wrapper -> {\n                    BackwardsBlockEntityProvider provider = Via.getManager().getProviders().get(BackwardsBlockEntityProvider.class);\n\n                    // TODO conduit handling\n                    if (wrapper.get(Types.UNSIGNED_BYTE, 0) == 5) {\n                        wrapper.cancel();\n                    }\n\n                    wrapper.set(Types.NAMED_COMPOUND_TAG, 0,\n                        provider.transform(\n                            wrapper.user(),\n                            wrapper.get(Types.BLOCK_POSITION1_8, 0),\n                            wrapper.get(Types.NAMED_COMPOUND_TAG, 0)\n                        ));\n                });\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_13.FORGET_LEVEL_CHUNK, wrapper -> {\n            int chunkMinX = wrapper.passthrough(Types.INT) << 4;\n            int chunkMinZ = wrapper.passthrough(Types.INT) << 4;\n            int chunkMaxX = chunkMinX + 15;\n            int chunkMaxZ = chunkMinZ + 15;\n            BackwardsBlockStorage blockStorage = wrapper.user().get(BackwardsBlockStorage.class);\n            blockStorage.getBlocks().entrySet().removeIf(entry -> {\n                BlockPosition position = entry.getKey();\n                return position.x() >= chunkMinX && position.z() >= chunkMinZ\n                    && position.x() <= chunkMaxX && position.z() <= chunkMaxZ;\n            });\n        });\n\n        // Block Change\n        protocol.registerClientbound(ClientboundPackets1_13.BLOCK_UPDATE, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.BLOCK_POSITION1_8); // 0 - Position\n\n                handler(wrapper -> {\n                    int blockState = wrapper.read(Types.VAR_INT);\n                    BlockPosition position = wrapper.get(Types.BLOCK_POSITION1_8, 0);\n\n                    // Note block special treatment\n                    if (blockState >= 249 && blockState <= 748) { // Note block states id range\n                        wrapper.user().get(NoteBlockStorage.class).storeNoteBlockUpdate(position, blockState);\n                    }\n\n                    // Store blocks\n                    BackwardsBlockStorage storage = wrapper.user().get(BackwardsBlockStorage.class);\n                    storage.checkAndStore(position, blockState);\n\n                    wrapper.write(Types.VAR_INT, protocol.getMappingData().getNewBlockStateId(blockState));\n\n                    // Flower pot special treatment\n                    flowerPotSpecialTreatment(wrapper.user(), blockState, position);\n                });\n            }\n        });\n\n        // Multi Block Change\n        protocol.registerClientbound(ClientboundPackets1_13.CHUNK_BLOCKS_UPDATE, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.INT); // 0 - Chunk X\n                map(Types.INT); // 1 - Chunk Z\n                map(Types.BLOCK_CHANGE_ARRAY);\n                handler(wrapper -> {\n                    BackwardsBlockStorage storage = wrapper.user().get(BackwardsBlockStorage.class);\n\n                    for (BlockChangeRecord record : wrapper.get(Types.BLOCK_CHANGE_ARRAY, 0)) {\n                        int chunkX = wrapper.get(Types.INT, 0);\n                        int chunkZ = wrapper.get(Types.INT, 1);\n                        int block = record.getBlockId();\n                        BlockPosition position = new BlockPosition(\n                            record.getSectionX() + (chunkX * 16),\n                            record.getY(),\n                            record.getSectionZ() + (chunkZ * 16));\n\n                        // Store if needed\n                        storage.checkAndStore(position, block);\n\n                        // Flower pot special treatment\n                        flowerPotSpecialTreatment(wrapper.user(), block, position);\n\n                        // Change to old id\n                        record.setBlockId(protocol.getMappingData().getNewBlockStateId(block));\n                    }\n                });\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_13.LEVEL_CHUNK, wrapper -> {\n            ClientWorld clientWorld = wrapper.user().getClientWorld(Protocol1_13To1_12_2.class);\n\n            ChunkType1_9_3 type_old = ChunkType1_9_3.forEnvironment(clientWorld.getEnvironment());\n            ChunkType1_13 type = ChunkType1_13.forEnvironment(clientWorld.getEnvironment());\n            Chunk chunk = wrapper.read(type);\n\n            // Handle Block Entities before block rewrite\n            BackwardsBlockEntityProvider provider = Via.getManager().getProviders().get(BackwardsBlockEntityProvider.class);\n            BackwardsBlockStorage storage = wrapper.user().get(BackwardsBlockStorage.class);\n            for (CompoundTag tag : chunk.getBlockEntities()) {\n                StringTag idTag = tag.getStringTag(\"id\");\n                if (idTag == null) continue;\n\n                String id = idTag.getValue();\n\n                // Ignore if we don't handle it\n                if (!provider.isHandled(id)) continue;\n\n                int sectionIndex = tag.getNumberTag(\"y\").asInt() >> 4;\n                if (sectionIndex < 0 || sectionIndex > 15) {\n                    // 1.17 chunks\n                    continue;\n                }\n\n                ChunkSection section = chunk.getSections()[sectionIndex];\n\n                int x = tag.getNumberTag(\"x\").asInt();\n                short y = tag.getNumberTag(\"y\").asShort();\n                int z = tag.getNumberTag(\"z\").asInt();\n                BlockPosition position = new BlockPosition(x, y, z);\n\n                int block = section.palette(PaletteType.BLOCKS).idAt(x & 0xF, y & 0xF, z & 0xF);\n                storage.checkAndStore(position, block);\n\n                provider.transform(wrapper.user(), position, tag);\n            }\n\n            // Rewrite new blocks to old blocks\n            for (int i = 0; i < chunk.getSections().length; i++) {\n                ChunkSection section = chunk.getSections()[i];\n                if (section == null) {\n                    continue;\n                }\n\n                DataPalette palette = section.palette(PaletteType.BLOCKS);\n                // Flower pots require a special treatment, they are no longer block entities :(\n                for (int y = 0; y < 16; y++) {\n                    for (int z = 0; z < 16; z++) {\n                        for (int x = 0; x < 16; x++) {\n                            int block = palette.idAt(x, y, z);\n\n                            // Check if the block is a flower\n                            if (FlowerPotHandler.isFlowah(block)) {\n                                BlockPosition pos = new BlockPosition(\n                                    (x + (chunk.getX() << 4)),\n                                    (short) (y + (i << 4)),\n                                    (z + (chunk.getZ() << 4))\n                                );\n                                // Store block\n                                storage.checkAndStore(pos, block);\n\n                                CompoundTag nbt = provider.transform(wrapper.user(), pos, \"minecraft:flower_pot\");\n\n                                chunk.getBlockEntities().add(nbt);\n                            }\n                        }\n                    }\n                }\n\n                for (int j = 0; j < palette.size(); j++) {\n                    int mappedBlockStateId = protocol.getMappingData().getNewBlockStateId(palette.idByIndex(j));\n                    palette.setIdByIndex(j, mappedBlockStateId);\n                }\n            }\n\n\n            if (chunk.isBiomeData()) {\n                for (int i = 0; i < 256; i++) {\n                    int biome = chunk.getBiomeData()[i];\n                    int newId = switch (biome) {\n                        case 40, 41, 42, 43 -> 9; // end biomes\n                        case 47, 48, 49 -> 24; // deep ocean biomes\n                        case 50 -> 10; // deep frozen... let's just pick the frozen variant\n                        case 44, 45, 46 -> 0; // the other new ocean biomes\n                        default -> -1;\n                    };\n\n                    if (newId != -1) {\n                        chunk.getBiomeData()[i] = newId;\n                    }\n                }\n            }\n\n            wrapper.write(type_old, chunk);\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_13.LEVEL_EVENT, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.INT); // Effect Id\n                map(Types.BLOCK_POSITION1_8); // Location\n                map(Types.INT); // Data\n                handler(wrapper -> {\n                    int id = wrapper.get(Types.INT, 0);\n                    int data = wrapper.get(Types.INT, 1);\n                    if (id == 1010) { // Play record\n                        wrapper.set(Types.INT, 1, protocol.getMappingData().getItemMappings().getNewId(data) >> 4);\n                    } else if (id == 2001) { // Block break + block break sound\n                        data = protocol.getMappingData().getNewBlockStateId(data);\n                        int blockId = data >> 4;\n                        int blockData = data & 0xF;\n                        wrapper.set(Types.INT, 1, (blockId & 0xFFF) | (blockData << 12));\n                    }\n                });\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_13.MAP_ITEM_DATA, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT);\n                map(Types.BYTE);\n                map(Types.BOOLEAN);\n                handler(wrapper -> {\n                    int iconCount = wrapper.passthrough(Types.VAR_INT);\n                    for (int i = 0; i < iconCount; i++) {\n                        int type = wrapper.read(Types.VAR_INT);\n                        byte x = wrapper.read(Types.BYTE);\n                        byte z = wrapper.read(Types.BYTE);\n                        byte direction = wrapper.read(Types.BYTE);\n                        wrapper.read(Types.OPTIONAL_COMPONENT);\n                        if (type > 9) {\n                            wrapper.set(Types.VAR_INT, 1, wrapper.get(Types.VAR_INT, 1) - 1);\n                            continue;\n                        }\n                        wrapper.write(Types.BYTE, (byte) ((type << 4) | (direction & 0x0F)));\n                        wrapper.write(Types.BYTE, x);\n                        wrapper.write(Types.BYTE, z);\n                    }\n                });\n            }\n        });\n    }\n\n    @Override\n    protected void registerRewrites() {\n        enchantmentMappings.put(\"minecraft:loyalty\", \"§7Loyalty\");\n        enchantmentMappings.put(\"minecraft:impaling\", \"§7Impaling\");\n        enchantmentMappings.put(\"minecraft:riptide\", \"§7Riptide\");\n        enchantmentMappings.put(\"minecraft:channeling\", \"§7Channeling\");\n    }\n\n    @Override\n    public Item handleItemToClient(UserConnection connection, Item item) {\n        if (item == null) return null;\n\n        // Custom mappings/super call moved down\n        int originalId = item.identifier();\n\n        Integer rawId = null;\n        boolean gotRawIdFromTag = false;\n\n        CompoundTag tag = item.tag();\n\n        // Use tag to get original ID and data\n        Tag originalIdTag;\n        if (tag != null && (originalIdTag = tag.remove(extraNbtTag)) instanceof NumberTag) {\n            rawId = ((NumberTag) originalIdTag).asInt();\n            gotRawIdFromTag = true;\n        }\n\n        if (rawId == null) {\n            // Look for custom mappings\n            item = super.handleItemToClient(connection, item);\n\n            // Handle one-way special case\n            if (item.identifier() == -1) {\n                if (originalId == 362) { // base/colorless shulker box\n                    rawId = 0xe50000; // purple shulker box\n                } else {\n                    if (Via.getConfig().logOtherConversionWarnings()) {\n                        protocol.getLogger().warning(\"Failed to get new item for \" + originalId);\n                    }\n\n                    rawId = 0x10000;\n                }\n            } else {  // Use the found custom mapping\n                // Take the newly added tag\n                if (tag == null) {\n                    tag = item.tag();\n                }\n\n                rawId = itemIdToRaw(item.identifier(), item, tag);\n            }\n        }\n\n        item.setIdentifier(rawId >> 16);\n        item.setData((short) (rawId & 0xFFFF));\n\n        // NBT changes\n        if (tag != null) {\n            if (isDamageable(item.identifier())) {\n                Tag damageTag = tag.remove(\"Damage\");\n                if (!gotRawIdFromTag && damageTag instanceof NumberTag) {\n                    item.setData(((NumberTag) damageTag).asShort());\n                }\n            }\n\n            if (item.identifier() == 358) { // map\n                Tag mapTag = tag.remove(\"map\");\n                if (!gotRawIdFromTag && mapTag instanceof NumberTag) {\n                    item.setData(((NumberTag) mapTag).asShort());\n                }\n            }\n\n            // Shield and banner\n            invertShieldAndBannerId(item, tag);\n\n            // Display Name now uses JSON\n            CompoundTag display = tag.getCompoundTag(\"display\");\n            if (display != null) {\n                StringTag name = display.getStringTag(\"Name\");\n                if (name != null) {\n                    display.putString(extraNbtTag + \"|Name\", name.getValue());\n                    name.setValue(protocol.jsonToLegacy(connection, name.getValue()));\n                }\n            }\n\n            // ench is now Enchantments and now uses identifiers\n            rewriteEnchantmentsToClient(tag, false);\n            rewriteEnchantmentsToClient(tag, true);\n\n            rewriteCanPlaceToClient(tag, \"CanPlaceOn\");\n            rewriteCanPlaceToClient(tag, \"CanDestroy\");\n        }\n        return item;\n    }\n\n    private int itemIdToRaw(int oldId, Item item, CompoundTag tag) {\n        Optional<String> eggEntityId = SpawnEggMappings1_13.getEntityId(oldId);\n        if (eggEntityId.isPresent()) {\n            if (tag == null) {\n                item.setTag(tag = new CompoundTag());\n            }\n            if (!tag.contains(\"EntityTag\")) {\n                CompoundTag entityTag = new CompoundTag();\n                entityTag.putString(\"id\", eggEntityId.get());\n                tag.put(\"EntityTag\", entityTag);\n            }\n            return 0x17f0000; // 383 << 16;\n        }\n\n        return (oldId >> 4) << 16 | oldId & 0xF;\n    }\n\n    private void rewriteCanPlaceToClient(CompoundTag tag, String tagName) {\n        // The tag was manually created incorrectly so ignore rewriting it\n        ListTag<?> blockTag = tag.getListTag(tagName);\n        if (blockTag == null) return;\n\n        ListTag<StringTag> newCanPlaceOn = new ListTag<>(StringTag.class);\n        tag.put(extraNbtTag + \"|\" + tagName, blockTag.copy());\n        for (Tag oldTag : blockTag) {\n            Object value = oldTag.getValue();\n            String[] newValues = value instanceof String ?\n                BlockIdData.fallbackReverseMapping.get(Key.stripMinecraftNamespace((String) value)) : null;\n            if (newValues != null) {\n                for (String newValue : newValues) {\n                    newCanPlaceOn.add(new StringTag(newValue));\n                }\n            } else {\n                newCanPlaceOn.add(new StringTag(oldTag.getValue().toString()));\n            }\n        }\n        tag.put(tagName, newCanPlaceOn);\n    }\n\n    //TODO un-ugly all of this\n    private void rewriteEnchantmentsToClient(CompoundTag tag, boolean storedEnch) {\n        String key = storedEnch ? \"StoredEnchantments\" : \"Enchantments\";\n        ListTag<CompoundTag> enchantments = tag.getListTag(key, CompoundTag.class);\n        if (enchantments == null) return;\n\n        ListTag<CompoundTag> noMapped = new ListTag<>(CompoundTag.class);\n        ListTag<CompoundTag> newEnchantments = new ListTag<>(CompoundTag.class);\n        List<StringTag> lore = new ArrayList<>();\n        boolean hasValidEnchants = false;\n        for (CompoundTag enchantmentEntry : enchantments.copy()) {\n            StringTag idTag = enchantmentEntry.getStringTag(\"id\");\n            if (idTag == null) {\n                continue;\n            }\n\n            String newId = idTag.getValue();\n            NumberTag levelTag = enchantmentEntry.getNumberTag(\"lvl\");\n            if (levelTag == null) {\n                continue;\n            }\n\n            int levelValue = levelTag.asInt();\n            short level = levelValue < Short.MAX_VALUE ? (short) levelValue : Short.MAX_VALUE;\n\n            String mappedEnchantmentId = enchantmentMappings.get(newId);\n            if (mappedEnchantmentId != null) {\n                lore.add(new StringTag(mappedEnchantmentId + \" \" + EnchantmentRewriter.getRomanNumber(level)));\n                noMapped.add(enchantmentEntry);\n            } else if (!newId.isEmpty()) {\n                Short oldId = Protocol1_12_2To1_13.MAPPINGS.getOldEnchantmentsIds().inverse().get(Key.stripMinecraftNamespace(newId));\n                if (oldId == null) {\n                    if (!newId.startsWith(\"viaversion:legacy/\")) {\n                        // Custom enchant (?)\n                        noMapped.add(enchantmentEntry);\n\n                        // Some custom-enchant plugins write it into the lore manually, which would double its entry\n                        if (ViaBackwards.getConfig().addCustomEnchantsToLore()) {\n                            String name = newId;\n                            int index = name.indexOf(':') + 1;\n                            if (index != 0 && index != name.length()) {\n                                name = name.substring(index);\n                            }\n                            name = \"§7\" + Character.toUpperCase(name.charAt(0)) + name.substring(1).toLowerCase(Locale.ENGLISH);\n\n                            lore.add(new StringTag(name + \" \" + EnchantmentRewriter.getRomanNumber(level)));\n                        }\n\n                        if (Via.getManager().isDebug()) {\n                            protocol.getLogger().warning(\"Found unknown enchant: \" + newId);\n                        }\n                        continue;\n                    } else {\n                        oldId = Short.valueOf(newId.substring(18));\n                    }\n                }\n\n                if (level != 0) {\n                    hasValidEnchants = true;\n                }\n\n                CompoundTag newEntry = new CompoundTag();\n                newEntry.putShort(\"id\", oldId);\n                newEntry.putShort(\"lvl\", level);\n                newEnchantments.add(newEntry);\n            }\n        }\n\n        // Put here to hide empty enchantment from 1.14 rewrites\n        if (!storedEnch && !hasValidEnchants) {\n            NumberTag hideFlags = tag.getNumberTag(\"HideFlags\");\n            if (hideFlags == null) {\n                hideFlags = new IntTag();\n                tag.put(extraNbtTag + \"|DummyEnchant\", new ByteTag(false));\n            } else {\n                tag.putInt(extraNbtTag + \"|OldHideFlags\", hideFlags.asByte());\n            }\n\n            if (newEnchantments.isEmpty()) {\n                CompoundTag enchEntry = new CompoundTag();\n                enchEntry.putShort(\"id\", (short) 0);\n                enchEntry.putShort(\"lvl\", (short) 0);\n                newEnchantments.add(enchEntry);\n            }\n\n            int value = hideFlags.asByte() | 1;\n            tag.putInt(\"HideFlags\", value);\n        }\n\n        if (!noMapped.isEmpty()) {\n            tag.put(extraNbtTag + \"|\" + key, noMapped);\n\n            if (!lore.isEmpty()) {\n                CompoundTag display = tag.getCompoundTag(\"display\");\n                if (display == null) {\n                    tag.put(\"display\", display = new CompoundTag());\n                }\n\n                ListTag<StringTag> loreTag = display.getListTag(\"Lore\", StringTag.class);\n                if (loreTag == null) {\n                    display.put(\"Lore\", loreTag = new ListTag<>(StringTag.class));\n                    tag.put(extraNbtTag + \"|DummyLore\", new ByteTag(false));\n                } else if (!loreTag.isEmpty()) {\n                    ListTag<StringTag> oldLore = new ListTag<>(StringTag.class);\n                    for (StringTag value : loreTag) {\n                        oldLore.add(value.copy());\n                    }\n                    tag.put(extraNbtTag + \"|OldLore\", oldLore);\n\n                    lore.addAll(loreTag.getValue());\n                }\n\n                loreTag.setValue(lore);\n            }\n        }\n\n        tag.remove(\"Enchantments\");\n        tag.put(storedEnch ? key : \"ench\", newEnchantments);\n    }\n\n    @Override\n    public Item handleItemToServer(UserConnection connection, Item item) {\n        if (item == null) return null;\n        CompoundTag tag = item.tag();\n\n        // Save original id\n        int originalId = (item.identifier() << 16 | item.data() & 0xFFFF);\n\n        int rawId = IdAndData.toRawData(item.identifier(), item.data());\n\n        // NBT Additions\n        if (isDamageable(item.identifier())) {\n            if (tag == null) item.setTag(tag = new CompoundTag());\n            tag.putInt(\"Damage\", item.data());\n        }\n        if (item.identifier() == 358) { // map\n            if (tag == null) item.setTag(tag = new CompoundTag());\n            tag.putInt(\"map\", item.data());\n        }\n\n        // NBT Changes\n        if (tag != null) {\n            // Shield and banner\n            invertShieldAndBannerId(item, tag);\n\n            // Display Name now uses JSON\n            CompoundTag display = tag.getCompoundTag(\"display\");\n            if (display != null) {\n                StringTag name = display.getStringTag(\"Name\");\n                if (name != null) {\n                    Tag via = display.remove(extraNbtTag + \"|Name\");\n                    name.setValue(via instanceof StringTag ? ((StringTag) via).getValue() : ComponentUtil.legacyToJsonString(name.getValue()));\n                }\n            }\n\n            // ench is now Enchantments and now uses identifiers\n            rewriteEnchantmentsToServer(tag, false);\n            rewriteEnchantmentsToServer(tag, true);\n\n            rewriteCanPlaceToServer(tag, \"CanPlaceOn\");\n            rewriteCanPlaceToServer(tag, \"CanDestroy\");\n\n            // Handle SpawnEggs\n            if (item.identifier() == 383) {\n                CompoundTag entityTag = tag.getCompoundTag(\"EntityTag\");\n                StringTag identifier;\n                if (entityTag != null && (identifier = entityTag.getStringTag(\"id\")) != null) {\n                    rawId = SpawnEggMappings1_13.getSpawnEggId(identifier.getValue());\n                    if (rawId == -1) {\n                        rawId = 25100288; // Bat fallback\n                    } else {\n                        entityTag.remove(\"id\");\n                        if (entityTag.isEmpty()) {\n                            tag.remove(\"EntityTag\");\n                        }\n                    }\n                } else {\n                    // Fallback to bat\n                    rawId = 25100288;\n                }\n            }\n            if (tag.isEmpty()) {\n                item.setTag(tag = null);\n            }\n        }\n\n        // Handle custom mappings\n        int identifier = item.identifier();\n        item.setIdentifier(rawId);\n        item = super.handleItemToServer(connection, item);\n\n        // Mapped with original data, we can return here\n        if (item.identifier() != rawId && item.identifier() != -1) return item;\n\n        // Set to legacy id again\n        item.setIdentifier(identifier);\n\n        int newId = -1;\n        if (protocol.getMappingData().getItemMappings().inverse().getNewId(rawId) == -1) {\n            if (!isDamageable(item.identifier()) && item.identifier() != 358) { // Map\n                if (tag == null) {\n                    item.setTag(tag = new CompoundTag());\n                }\n                tag.putInt(extraNbtTag, originalId); // Data will be lost, saving original id\n            }\n\n            if (item.identifier() == 229) { // purple shulker box\n                newId = 362; // directly set the new id -> base/colorless shulker box\n            } else if (item.identifier() == 31 && item.data() == 0) { // Shrub was removed\n                rawId = IdAndData.toRawData(32); // Dead Bush\n            } else if (protocol.getMappingData().getItemMappings().inverse().getNewId(rawId & ~0xF) != -1) {\n                rawId &= ~0xF; // Remove data\n            } else {\n                if (Via.getConfig().logOtherConversionWarnings()) {\n                    protocol.getLogger().warning(\"Failed to get old item for \" + item.identifier());\n                }\n                rawId = 16; // Stone\n            }\n        }\n\n        if (newId == -1) {\n            newId = protocol.getMappingData().getItemMappings().inverse().getNewId(rawId);\n        }\n\n        item.setIdentifier(newId);\n        item.setData((short) 0);\n        return item;\n    }\n\n    private void rewriteCanPlaceToServer(CompoundTag tag, String tagName) {\n        if (tag.getListTag(tagName) == null) return;\n\n        ListTag<?> blockTag = tag.getListTag(extraNbtTag + \"|\" + tagName);\n        if (blockTag != null) {\n            tag.remove(extraNbtTag + \"|\" + tagName);\n            tag.put(tagName, blockTag.copy());\n        } else if ((blockTag = tag.getListTag(tagName)) != null) {\n            ListTag<StringTag> newCanPlaceOn = new ListTag<>(StringTag.class);\n            for (Tag oldTag : blockTag) {\n                Object value = oldTag.getValue();\n                String oldId = Key.stripMinecraftNamespace(value.toString());\n                int key = Ints.tryParse(oldId);\n                String numberConverted = BlockIdData.numberIdToString.get(key);\n                if (numberConverted != null) {\n                    oldId = numberConverted;\n                }\n\n                String lowerCaseId = oldId.toLowerCase(Locale.ROOT);\n                String[] newValues = BlockIdData.blockIdMapping.get(lowerCaseId);\n                if (newValues != null) {\n                    for (String newValue : newValues) {\n                        newCanPlaceOn.add(new StringTag(newValue));\n                    }\n                } else {\n                    newCanPlaceOn.add(new StringTag(lowerCaseId));\n                }\n            }\n            tag.put(tagName, newCanPlaceOn);\n        }\n    }\n\n    private void rewriteEnchantmentsToServer(CompoundTag tag, boolean storedEnch) {\n        String key = storedEnch ? \"StoredEnchantments\" : \"Enchantments\";\n        ListTag<CompoundTag> enchantments = tag.getListTag(storedEnch ? key : \"ench\", CompoundTag.class);\n        if (enchantments == null) return;\n\n        ListTag<CompoundTag> newEnchantments = new ListTag<>(CompoundTag.class);\n        boolean dummyEnchant = false;\n        if (!storedEnch) {\n            Tag hideFlags = tag.remove(extraNbtTag + \"|OldHideFlags\");\n            if (hideFlags instanceof IntTag) {\n                tag.putInt(\"HideFlags\", ((NumberTag) hideFlags).asByte());\n                dummyEnchant = true;\n            } else if (tag.remove(extraNbtTag + \"|DummyEnchant\") != null) {\n                tag.remove(\"HideFlags\");\n                dummyEnchant = true;\n            }\n        }\n\n        for (CompoundTag entryTag : enchantments) {\n            NumberTag idTag = entryTag.getNumberTag(\"id\");\n            NumberTag levelTag = entryTag.getNumberTag(\"lvl\");\n            CompoundTag enchantmentEntry = new CompoundTag();\n            short oldId = idTag != null ? idTag.asShort() : 0;\n            short level = levelTag != null ? levelTag.asShort() : 0;\n            if (dummyEnchant && oldId == 0 && level == 0) {\n                continue; // Skip dummy enchatment\n            }\n\n            String newId = Protocol1_12_2To1_13.MAPPINGS.getOldEnchantmentsIds().get(oldId);\n            if (newId == null) {\n                newId = \"viaversion:legacy/\" + oldId;\n            }\n            enchantmentEntry.putString(\"id\", newId);\n\n            enchantmentEntry.putShort(\"lvl\", level);\n            newEnchantments.add(enchantmentEntry);\n        }\n\n        ListTag<CompoundTag> noMapped = tag.getListTag(extraNbtTag + \"|Enchantments\", CompoundTag.class);\n        if (noMapped != null) {\n            for (CompoundTag value : noMapped) {\n                newEnchantments.add(value);\n            }\n            tag.remove(extraNbtTag + \"|Enchantments\");\n        }\n\n        CompoundTag display = tag.getCompoundTag(\"display\");\n        if (display == null) {\n            tag.put(\"display\", display = new CompoundTag());\n        }\n\n        ListTag<StringTag> oldLore = tag.getListTag(extraNbtTag + \"|OldLore\", StringTag.class);\n        if (oldLore != null) {\n            ListTag<StringTag> lore = display.getListTag(\"Lore\", StringTag.class);\n            if (lore == null) {\n                tag.put(\"Lore\", lore = new ListTag<>(StringTag.class));\n            }\n\n            lore.setValue(oldLore.getValue());\n            tag.remove(extraNbtTag + \"|OldLore\");\n        } else if (tag.remove(extraNbtTag + \"|DummyLore\") != null) {\n            display.remove(\"Lore\");\n            if (display.isEmpty()) {\n                tag.remove(\"display\");\n            }\n        }\n\n        if (!storedEnch) {\n            tag.remove(\"ench\");\n        }\n        tag.put(key, newEnchantments);\n    }\n\n    private void invertShieldAndBannerId(Item item, CompoundTag tag) {\n        if (item.identifier() != 442 && item.identifier() != 425) return;\n\n        CompoundTag blockEntityTag = tag.getCompoundTag(\"BlockEntityTag\");\n        if (blockEntityTag == null) return;\n\n        NumberTag base = blockEntityTag.getNumberTag(\"Base\");\n        if (base != null) {\n            blockEntityTag.putInt(\"Base\", 15 - base.asInt()); // Invert color id\n        }\n\n        ListTag<CompoundTag> patterns = blockEntityTag.getListTag(\"Patterns\", CompoundTag.class);\n        if (patterns != null) {\n            for (CompoundTag pattern : patterns) {\n                NumberTag colorTag = pattern.getNumberTag(\"Color\");\n                pattern.putInt(\"Color\", 15 - colorTag.asInt()); // Invert color id\n            }\n        }\n    }\n\n    // TODO find a less hacky way to do this (https://bugs.mojang.com/browse/MC-74231)\n    private static void flowerPotSpecialTreatment(UserConnection user, int blockState, BlockPosition position) {\n        if (FlowerPotHandler.isFlowah(blockState)) {\n            BackwardsBlockEntityProvider beProvider = Via.getManager().getProviders().get(BackwardsBlockEntityProvider.class);\n\n            CompoundTag nbt = beProvider.transform(user, position, \"minecraft:flower_pot\");\n\n            // Remove the flowerpot\n            PacketWrapper blockUpdateRemove = PacketWrapper.create(ClientboundPackets1_12_1.BLOCK_UPDATE, user);\n            blockUpdateRemove.write(Types.BLOCK_POSITION1_8, position);\n            blockUpdateRemove.write(Types.VAR_INT, 0);\n            blockUpdateRemove.scheduleSend(Protocol1_13To1_12_2.class);\n\n            // Create the flowerpot\n            PacketWrapper blockCreate = PacketWrapper.create(ClientboundPackets1_12_1.BLOCK_UPDATE, user);\n            blockCreate.write(Types.BLOCK_POSITION1_8, position);\n            blockCreate.write(Types.VAR_INT, Protocol1_13To1_12_2.MAPPINGS.getNewBlockStateId(blockState));\n            blockCreate.scheduleSend(Protocol1_13To1_12_2.class);\n\n            // Send a block entity update\n            PacketWrapper wrapper = PacketWrapper.create(ClientboundPackets1_12_1.BLOCK_ENTITY_DATA, user);\n            wrapper.write(Types.BLOCK_POSITION1_8, position);\n            wrapper.write(Types.UNSIGNED_BYTE, (short) 5);\n            wrapper.write(Types.NAMED_COMPOUND_TAG, nbt);\n            wrapper.scheduleSend(Protocol1_13To1_12_2.class);\n\n        }\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_13to1_12_2/rewriter/EntityPacketRewriter1_13.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_13to1_12_2.rewriter;\n\nimport com.viaversion.viabackwards.ViaBackwards;\nimport com.viaversion.viabackwards.api.entities.storage.EntityPositionHandler;\nimport com.viaversion.viabackwards.api.rewriters.LegacyEntityRewriter;\nimport com.viaversion.viabackwards.protocol.v1_13to1_12_2.Protocol1_13To1_12_2;\nimport com.viaversion.viabackwards.protocol.v1_13to1_12_2.data.EntityIdMappings1_12_2;\nimport com.viaversion.viabackwards.protocol.v1_13to1_12_2.data.PaintingNames1_13;\nimport com.viaversion.viabackwards.protocol.v1_13to1_12_2.data.ParticleIdMappings1_12_2;\nimport com.viaversion.viabackwards.protocol.v1_13to1_12_2.storage.BackwardsBlockStorage;\nimport com.viaversion.viabackwards.protocol.v1_13to1_12_2.storage.NoteBlockStorage;\nimport com.viaversion.viabackwards.protocol.v1_13to1_12_2.storage.PlayerPositionStorage1_13;\nimport com.viaversion.viaversion.api.minecraft.ClientWorld;\nimport com.viaversion.viaversion.api.minecraft.Particle;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityType;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_13;\nimport com.viaversion.viaversion.api.minecraft.entitydata.EntityData;\nimport com.viaversion.viaversion.api.minecraft.entitydata.types.EntityDataTypes1_12;\nimport com.viaversion.viaversion.api.minecraft.item.Item;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandler;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.version.Types1_13;\nimport com.viaversion.viaversion.libs.gson.JsonElement;\nimport com.viaversion.viaversion.protocols.v1_12_2to1_13.packet.ClientboundPackets1_13;\nimport com.viaversion.viaversion.protocols.v1_12to1_12_1.packet.ClientboundPackets1_12_1;\nimport com.viaversion.viaversion.protocols.v1_12to1_12_1.packet.ServerboundPackets1_12_1;\n\npublic class EntityPacketRewriter1_13 extends LegacyEntityRewriter<ClientboundPackets1_13, Protocol1_13To1_12_2> {\n\n    public EntityPacketRewriter1_13(Protocol1_13To1_12_2 protocol) {\n        super(protocol);\n    }\n\n    @Override\n    protected void registerPackets() {\n        protocol.registerClientbound(ClientboundPackets1_13.PLAYER_POSITION, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.DOUBLE);\n                map(Types.DOUBLE);\n                map(Types.DOUBLE);\n                map(Types.FLOAT);\n                map(Types.FLOAT);\n                map(Types.BYTE);\n                handler(wrapper -> {\n                    if (!ViaBackwards.getConfig().isFix1_13FacePlayer()) return;\n\n                    PlayerPositionStorage1_13 playerStorage = wrapper.user().get(PlayerPositionStorage1_13.class);\n                    byte bitField = wrapper.get(Types.BYTE, 0);\n                    playerStorage.setX(toSet(bitField, 0, playerStorage.x(), wrapper.get(Types.DOUBLE, 0)));\n                    playerStorage.setY(toSet(bitField, 1, playerStorage.y(), wrapper.get(Types.DOUBLE, 1)));\n                    playerStorage.setZ(toSet(bitField, 2, playerStorage.z(), wrapper.get(Types.DOUBLE, 2)));\n                });\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_13.ADD_ENTITY, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT);\n                map(Types.UUID);\n                map(Types.BYTE);\n                map(Types.DOUBLE);\n                map(Types.DOUBLE);\n                map(Types.DOUBLE);\n                map(Types.BYTE);\n                map(Types.BYTE);\n                map(Types.INT);\n\n                handler(getObjectTrackerHandler());\n\n                handler(wrapper -> {\n                    int id = wrapper.get(Types.BYTE, 0);\n                    int data = wrapper.get(Types.INT, 0);\n                    EntityTypes1_13.ObjectType type = EntityTypes1_13.ObjectType.findById(id, data);\n                    if (type == EntityTypes1_13.ObjectType.FALLING_BLOCK) {\n                        int combined = Protocol1_13To1_12_2.MAPPINGS.getNewBlockStateId(data);\n                        combined = ((combined >> 4) & 0xFFF) | ((combined & 0xF) << 12);\n                        wrapper.set(Types.INT, 0, combined);\n                    } else if (type == EntityTypes1_13.ObjectType.ITEM_FRAME) {\n                        data = switch (data) {\n                            case 3 -> 0;\n                            case 4 -> 1;\n                            case 5 -> 3;\n                            default -> data;\n                        };\n                        wrapper.set(Types.INT, 0, data);\n                    } else if (type == EntityTypes1_13.ObjectType.TRIDENT) {\n                        wrapper.set(Types.BYTE, 0, (byte) EntityTypes1_13.ObjectType.TIPPED_ARROW.getId());\n                    }\n                });\n            }\n        });\n\n        registerTracker(ClientboundPackets1_13.ADD_EXPERIENCE_ORB, EntityTypes1_13.EntityType.EXPERIENCE_ORB);\n        registerTracker(ClientboundPackets1_13.ADD_GLOBAL_ENTITY, EntityTypes1_13.EntityType.LIGHTNING_BOLT);\n\n        protocol.registerClientbound(ClientboundPackets1_13.ADD_MOB, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT);\n                map(Types.UUID);\n                map(Types.VAR_INT);\n                map(Types.DOUBLE);\n                map(Types.DOUBLE);\n                map(Types.DOUBLE);\n                map(Types.BYTE);\n                map(Types.BYTE);\n                map(Types.BYTE);\n                map(Types.SHORT);\n                map(Types.SHORT);\n                map(Types.SHORT);\n                map(Types1_13.ENTITY_DATA_LIST, Types.ENTITY_DATA_LIST1_12);\n\n                handler(wrapper -> {\n                    int type = wrapper.get(Types.VAR_INT, 1);\n                    EntityType entityType = EntityTypes1_13.EntityType.findById(type);\n                    if (entityType == null) {\n                        return;\n                    }\n\n                    tracker(wrapper.user()).addEntity(wrapper.get(Types.VAR_INT, 0), entityType);\n\n                    int oldId = EntityIdMappings1_12_2.getOldId(type);\n                    if (oldId == -1) {\n                        if (!hasData(entityType)) {\n                            protocol.getLogger().warning(\"Could not find entity type mapping \" + type + \"/\" + entityType);\n                        }\n                    } else {\n                        wrapper.set(Types.VAR_INT, 1, oldId);\n                    }\n                });\n\n                // Rewrite entity type / ddata\n                handler(getMobSpawnRewriter1_11(Types.ENTITY_DATA_LIST1_12));\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_13.ADD_PLAYER, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT);\n                map(Types.UUID);\n                map(Types.DOUBLE);\n                map(Types.DOUBLE);\n                map(Types.DOUBLE);\n                map(Types.BYTE);\n                map(Types.BYTE);\n                map(Types1_13.ENTITY_DATA_LIST, Types.ENTITY_DATA_LIST1_12);\n\n                handler(getTrackerAndDataHandler(Types.ENTITY_DATA_LIST1_12, EntityTypes1_13.EntityType.PLAYER));\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_13.ADD_PAINTING, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT);\n                map(Types.UUID);\n\n                handler(getTrackerHandler(EntityTypes1_13.EntityType.PAINTING));\n                handler(wrapper -> {\n                    int motive = wrapper.read(Types.VAR_INT);\n                    String title = PaintingNames1_13.getStringId(motive);\n                    wrapper.write(Types.STRING, title);\n                });\n            }\n        });\n\n        registerJoinGame(ClientboundPackets1_13.LOGIN, EntityTypes1_13.EntityType.PLAYER);\n\n        protocol.registerClientbound(ClientboundPackets1_13.RESPAWN, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.INT); // 0 - Dimension ID\n\n                handler(wrapper -> {\n                    ClientWorld clientWorld = wrapper.user().getClientWorld(Protocol1_13To1_12_2.class);\n                    int dimensionId = wrapper.get(Types.INT, 0);\n\n                    if (clientWorld.setEnvironment(dimensionId)) {\n                        tracker(wrapper.user()).clearEntities();\n                        wrapper.user().get(BackwardsBlockStorage.class).clear();\n                        wrapper.user().get(NoteBlockStorage.class).clear();\n                    }\n                });\n            }\n        });\n\n        registerSetEntityData(ClientboundPackets1_13.SET_ENTITY_DATA, Types1_13.ENTITY_DATA_LIST, Types.ENTITY_DATA_LIST1_12);\n\n        // Face Player (new packet)\n        protocol.registerClientbound(ClientboundPackets1_13.PLAYER_LOOK_AT, null, wrapper -> {\n            wrapper.cancel();\n\n            if (!ViaBackwards.getConfig().isFix1_13FacePlayer()) return;\n\n            // We will just accept a possible, very minor mismatch between server and client position,\n            // and will take the server's one in both cases, else we would have to cache all entities' positions.\n            final int anchor = wrapper.read(Types.VAR_INT); // feet/eyes enum\n            final double x = wrapper.read(Types.DOUBLE);\n            final double y = wrapper.read(Types.DOUBLE);\n            final double z = wrapper.read(Types.DOUBLE);\n\n            PlayerPositionStorage1_13 positionStorage = wrapper.user().get(PlayerPositionStorage1_13.class);\n\n            // Send teleport packet to client\n            PacketWrapper positionAndLook = wrapper.create(ClientboundPackets1_12_1.PLAYER_POSITION);\n            positionAndLook.write(Types.DOUBLE, 0D);\n            positionAndLook.write(Types.DOUBLE, 0D);\n            positionAndLook.write(Types.DOUBLE, 0D);\n\n            //TODO properly cache and calculate head position?\n            EntityPositionHandler.writeFacingDegrees(positionAndLook, positionStorage.x(),\n                anchor == 1 ? positionStorage.y() + 1.62 : positionStorage.y(),\n                positionStorage.z(), x, y, z);\n\n            positionAndLook.write(Types.BYTE, (byte) 7); // bitfield, 0=absolute, 1=relative - x,y,z relative, yaw,pitch absolute\n            positionAndLook.write(Types.VAR_INT, -1);\n            positionAndLook.send(Protocol1_13To1_12_2.class);\n        });\n\n        PacketHandler movementRemapper = wrapper -> {\n            if (!ViaBackwards.getConfig().isFix1_13FacePlayer()) {\n                return;\n            }\n            final double x = wrapper.passthrough(Types.DOUBLE);\n            final double y = wrapper.passthrough(Types.DOUBLE);\n            final double z = wrapper.passthrough(Types.DOUBLE);\n            wrapper.user().get(PlayerPositionStorage1_13.class).setPosition(x, y, z);\n        };\n        protocol.registerServerbound(ServerboundPackets1_12_1.MOVE_PLAYER_POS, movementRemapper); // Player Position\n        protocol.registerServerbound(ServerboundPackets1_12_1.MOVE_PLAYER_POS_ROT, movementRemapper); // Player Position And Look (serverbound)\n        protocol.registerServerbound(ServerboundPackets1_12_1.MOVE_VEHICLE, movementRemapper); // Vehicle Move (serverbound)\n    }\n\n    @Override\n    protected void registerRewrites() {\n        // Rewrite new Entity 'drowned'\n        mapEntityTypeWithData(EntityTypes1_13.EntityType.DROWNED, EntityTypes1_13.EntityType.ZOMBIE_VILLAGER).plainName();\n\n        // Fishy\n        mapEntityTypeWithData(EntityTypes1_13.EntityType.COD, EntityTypes1_13.EntityType.SQUID).plainName();\n        mapEntityTypeWithData(EntityTypes1_13.EntityType.SALMON, EntityTypes1_13.EntityType.SQUID).plainName();\n        mapEntityTypeWithData(EntityTypes1_13.EntityType.PUFFERFISH, EntityTypes1_13.EntityType.SQUID).plainName();\n        mapEntityTypeWithData(EntityTypes1_13.EntityType.TROPICAL_FISH, EntityTypes1_13.EntityType.SQUID).plainName();\n\n        // Phantom\n        mapEntityTypeWithData(EntityTypes1_13.EntityType.PHANTOM, EntityTypes1_13.EntityType.PARROT).plainName().spawnEntityData(storage -> {\n            // The phantom is grey/blue so let's do yellow/blue\n            storage.add(new EntityData(15, EntityDataTypes1_12.VAR_INT, 3));\n        });\n\n        // Dolphin\n        mapEntityTypeWithData(EntityTypes1_13.EntityType.DOLPHIN, EntityTypes1_13.EntityType.SQUID).plainName();\n\n        // Turtle\n        mapEntityTypeWithData(EntityTypes1_13.EntityType.TURTLE, EntityTypes1_13.EntityType.OCELOT).plainName();\n\n        // Rewrite Data types\n        filter().handler((event, data) -> {\n            int typeId = data.dataType().typeId();\n            if (typeId == 4) {\n                JsonElement element = data.value();\n                protocol.translatableRewriter().processText(event.user(), element);\n                data.setDataType(EntityDataTypes1_12.COMPONENT);\n            } else if (typeId == 5) {\n                // Rewrite optional chat to string\n                JsonElement element = data.value();\n                data.setTypeAndValue(EntityDataTypes1_12.STRING, protocol.jsonToLegacy(event.user(), element));\n            } else if (typeId == 6) {\n                Item item = (Item) data.getValue();\n                data.setTypeAndValue(EntityDataTypes1_12.ITEM, protocol.getItemRewriter().handleItemToClient(event.user(), item));\n            } else if (typeId == 15) {\n                // Discontinue particles\n                event.cancel();\n            } else {\n                data.setDataType(EntityDataTypes1_12.byId(typeId > 5 ? typeId - 1 : typeId));\n            }\n        });\n\n        // Handle zombie entity data\n        filter().type(EntityTypes1_13.EntityType.ZOMBIE).removeIndex(15);\n\n        // Handle turtle entity data (Remove them all for now)\n        filter().type(EntityTypes1_13.EntityType.TURTLE).cancel(13); // Home pos\n        filter().type(EntityTypes1_13.EntityType.TURTLE).cancel(14); // Has egg\n        filter().type(EntityTypes1_13.EntityType.TURTLE).cancel(15); // Laying egg\n        filter().type(EntityTypes1_13.EntityType.TURTLE).cancel(16); // Travel pos\n        filter().type(EntityTypes1_13.EntityType.TURTLE).cancel(17); // Going home\n        filter().type(EntityTypes1_13.EntityType.TURTLE).cancel(18); // Traveling\n\n        // Remove additional fish data\n        filter().type(EntityTypes1_13.EntityType.ABSTRACT_FISH).cancel(12);\n        filter().type(EntityTypes1_13.EntityType.ABSTRACT_FISH).cancel(13);\n\n        // Remove phantom size\n        filter().type(EntityTypes1_13.EntityType.PHANTOM).cancel(12);\n\n        // Remove boat splash timer\n        filter().type(EntityTypes1_13.EntityType.BOAT).cancel(12);\n\n        // Remove Trident special loyalty level\n        filter().type(EntityTypes1_13.EntityType.TRIDENT).cancel(7);\n\n        // Handle new wolf colors\n        filter().type(EntityTypes1_13.EntityType.WOLF).index(17).handler((event, data) -> {\n            data.setValue(15 - (int) data.getValue());\n        });\n\n        // Rewrite AreaEffectCloud\n        filter().type(EntityTypes1_13.EntityType.AREA_EFFECT_CLOUD).index(9).handler((event, data) -> {\n            Particle particle = (Particle) data.getValue();\n\n            ParticleIdMappings1_12_2.ParticleData particleData = ParticleIdMappings1_12_2.getMapping(particle.id());\n\n            int firstArg = 0;\n            int secondArg = 0;\n            int[] particleArgs = particleData.rewriteMeta(protocol, particle.getArguments());\n            if (particleArgs != null && particleArgs.length != 0) {\n                if (particleData.getHandler().isBlockHandler() && particleArgs[0] == 0) {\n                    // Air doesn't have a break particle for sub 1.13 clients -> glass pane\n                    particleArgs[0] = 102;\n                }\n\n                firstArg = particleArgs[0];\n                secondArg = particleArgs.length == 2 ? particleArgs[1] : 0;\n            }\n\n            event.createExtraData(new EntityData(9, EntityDataTypes1_12.VAR_INT, particleData.getHistoryId()));\n            event.createExtraData(new EntityData(10, EntityDataTypes1_12.VAR_INT, firstArg));\n            event.createExtraData(new EntityData(11, EntityDataTypes1_12.VAR_INT, secondArg));\n\n            event.cancel();\n        });\n    }\n\n    @Override\n    public EntityType typeFromId(int typeId) {\n        return EntityTypes1_13.EntityType.findById(typeId);\n    }\n\n    @Override\n    public EntityType objectTypeFromId(int typeId, int data) {\n        return EntityTypes1_13.ObjectType.getEntityType(typeId, data);\n    }\n\n    @Override\n    public int newEntityId(final int newId) {\n        return EntityIdMappings1_12_2.getOldId(newId);\n    }\n\n    private static double toSet(int field, int bitIndex, double origin, double packetValue) {\n        // If bit is set, coordinate is relative\n        return (field & (1 << bitIndex)) != 0 ? origin + packetValue : packetValue;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_13to1_12_2/rewriter/PlayerPacketRewriter1_13.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_13to1_12_2.rewriter;\n\nimport com.google.common.base.Joiner;\nimport com.viaversion.viabackwards.ViaBackwards;\nimport com.viaversion.viabackwards.protocol.v1_13to1_12_2.Protocol1_13To1_12_2;\nimport com.viaversion.viabackwards.protocol.v1_13to1_12_2.data.ParticleIdMappings1_12_2;\nimport com.viaversion.viabackwards.protocol.v1_13to1_12_2.storage.TabCompleteStorage;\nimport com.viaversion.viabackwards.utils.ChatUtil;\nimport com.viaversion.viaversion.api.Via;\nimport com.viaversion.viaversion.api.minecraft.BlockPosition;\nimport com.viaversion.viaversion.api.minecraft.item.Item;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.protocol.packet.State;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.rewriter.RewriterBase;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.libs.gson.JsonElement;\nimport com.viaversion.viaversion.protocols.base.ClientboundLoginPackets;\nimport com.viaversion.viaversion.protocols.base.ServerboundLoginPackets;\nimport com.viaversion.viaversion.protocols.v1_12_2to1_13.packet.ClientboundPackets1_13;\nimport com.viaversion.viaversion.protocols.v1_12_2to1_13.packet.ServerboundPackets1_13;\nimport com.viaversion.viaversion.protocols.v1_12_2to1_13.rewriter.ItemPacketRewriter1_13;\nimport com.viaversion.viaversion.protocols.v1_12to1_12_1.packet.ClientboundPackets1_12_1;\nimport com.viaversion.viaversion.protocols.v1_12to1_12_1.packet.ServerboundPackets1_12_1;\nimport com.viaversion.viaversion.rewriter.CommandRewriter;\nimport com.viaversion.viaversion.util.Key;\nimport java.nio.charset.StandardCharsets;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.UUID;\nimport java.util.concurrent.ThreadLocalRandom;\n\npublic class PlayerPacketRewriter1_13 extends RewriterBase<Protocol1_13To1_12_2> {\n\n    private final CommandRewriter<ClientboundPackets1_13> commandRewriter = new CommandRewriter<>(protocol);\n\n    public PlayerPacketRewriter1_13(Protocol1_13To1_12_2 protocol) {\n        super(protocol);\n    }\n\n    @Override\n    protected void registerPackets() {\n        // Login Plugin Request\n        protocol.registerClientbound(State.LOGIN, ClientboundLoginPackets.CUSTOM_QUERY.getId(), -1, new PacketHandlers() {\n            @Override\n            public void register() {\n                handler(packetWrapper -> {\n                    packetWrapper.cancel();\n                    // Plugin response\n                    packetWrapper.create(ServerboundLoginPackets.CUSTOM_QUERY_ANSWER.getId(), wrapper -> {\n                        wrapper.write(Types.VAR_INT, packetWrapper.read(Types.VAR_INT)); // Packet id\n                        wrapper.write(Types.BOOLEAN, false); // Success\n                    }).sendToServer(Protocol1_13To1_12_2.class);\n                });\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_13.CUSTOM_PAYLOAD, wrapper -> {\n            String channel = wrapper.read(Types.STRING);\n            if (channel.equals(\"minecraft:trader_list\")) {\n                wrapper.write(Types.STRING, \"MC|TrList\");\n                protocol.getItemRewriter().handleTradeList(wrapper);\n            } else {\n                String oldChannel = ItemPacketRewriter1_13.getOldPluginChannelId(channel);\n                if (oldChannel == null) {\n                    if (Via.getConfig().logOtherConversionWarnings()) {\n                        protocol.getLogger().warning(\"Ignoring clientbound plugin message with channel: \" + channel);\n                    }\n                    wrapper.cancel();\n                    return;\n                }\n                wrapper.write(Types.STRING, oldChannel);\n\n                if (oldChannel.equals(\"REGISTER\") || oldChannel.equals(\"UNREGISTER\")) {\n                    String[] channels = new String(wrapper.read(Types.REMAINING_BYTES), StandardCharsets.UTF_8).split(\"\\0\");\n                    List<String> rewrittenChannels = new ArrayList<>();\n                    for (String s : channels) {\n                        String rewritten = ItemPacketRewriter1_13.getOldPluginChannelId(s);\n                        if (rewritten != null) {\n                            rewrittenChannels.add(rewritten);\n                        } else if (Via.getConfig().logOtherConversionWarnings()) {\n                            protocol.getLogger().warning(\"Ignoring plugin channel in clientbound \" + oldChannel + \": \" + s);\n                        }\n                    }\n                    wrapper.write(Types.REMAINING_BYTES, Joiner.on('\\0').join(rewrittenChannels).getBytes(StandardCharsets.UTF_8));\n                }\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_13.LEVEL_PARTICLES, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.INT);      // 0 - Particle ID\n                map(Types.BOOLEAN);  // 1 - Long Distance\n                map(Types.FLOAT);    // 2 - X\n                map(Types.FLOAT);    // 3 - Y\n                map(Types.FLOAT);    // 4 - Z\n                map(Types.FLOAT);    // 5 - Offset X\n                map(Types.FLOAT);    // 6 - Offset Y\n                map(Types.FLOAT);    // 7 - Offset Z\n                map(Types.FLOAT);    // 8 - Particle Data\n                map(Types.INT);      // 9 - Particle Count\n                handler(wrapper -> {\n                    ParticleIdMappings1_12_2.ParticleData old = ParticleIdMappings1_12_2.getMapping(wrapper.get(Types.INT, 0));\n                    wrapper.set(Types.INT, 0, old.getHistoryId());\n\n                    int[] data = old.rewriteData(protocol, wrapper);\n                    if (data != null) {\n                        if (old.getHandler().isBlockHandler() && data[0] == 0) {\n                            // Cancel air block particles\n                            wrapper.cancel();\n                            return;\n                        }\n\n                        for (int i : data) {\n                            wrapper.write(Types.VAR_INT, i);\n                        }\n                    }\n                });\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_13.PLAYER_INFO, new PacketHandlers() {\n            @Override\n            public void register() {\n                handler(packetWrapper -> {\n                    TabCompleteStorage storage = packetWrapper.user().get(TabCompleteStorage.class);\n                    int action = packetWrapper.passthrough(Types.VAR_INT);\n                    int nPlayers = packetWrapper.passthrough(Types.VAR_INT);\n                    for (int i = 0; i < nPlayers; i++) {\n                        UUID uuid = packetWrapper.passthrough(Types.UUID);\n                        if (action == 0) { // Add\n                            String name = packetWrapper.passthrough(Types.STRING);\n                            storage.usernames().put(uuid, name);\n                            packetWrapper.passthrough(Types.PROFILE_PROPERTY_ARRAY);\n                            packetWrapper.passthrough(Types.VAR_INT);\n                            packetWrapper.passthrough(Types.VAR_INT);\n                            packetWrapper.passthrough(Types.OPTIONAL_COMPONENT);\n                        } else if (action == 1) { // Update Game Mode\n                            packetWrapper.passthrough(Types.VAR_INT);\n                        } else if (action == 2) { // Update Ping\n                            packetWrapper.passthrough(Types.VAR_INT);\n                        } else if (action == 3) { // Update Display Name\n                            packetWrapper.passthrough(Types.OPTIONAL_COMPONENT);\n                        } else if (action == 4) { // Remove Player\n                            storage.usernames().remove(uuid);\n                        }\n                    }\n                });\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_13.SET_OBJECTIVE, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.STRING);\n                map(Types.BYTE);\n                handler(wrapper -> {\n                    byte mode = wrapper.get(Types.BYTE, 0);\n                    if (mode == 0 || mode == 2) {\n                        JsonElement value = wrapper.read(Types.COMPONENT);\n                        String legacyValue = protocol.jsonToLegacy(wrapper.user(), value);\n                        wrapper.write(Types.STRING, ChatUtil.fromLegacy(legacyValue, 'f', 32));\n                        int type = wrapper.read(Types.VAR_INT);\n                        wrapper.write(Types.STRING, type == 1 ? \"hearts\" : \"integer\");\n                    }\n                });\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_13.SET_PLAYER_TEAM, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.STRING); // Name\n                map(Types.BYTE); // Action\n                handler(wrapper -> {\n                    byte action = wrapper.get(Types.BYTE, 0);\n                    if (action == 0 || action == 2) {\n                        JsonElement displayName = wrapper.read(Types.COMPONENT);\n                        String legacyTextDisplayName = protocol.jsonToLegacy(wrapper.user(), displayName);\n                        wrapper.write(Types.STRING, ChatUtil.fromLegacy(legacyTextDisplayName, 'f', 32));\n\n                        byte flags = wrapper.read(Types.BYTE);\n                        String nameTagVisibility = wrapper.read(Types.STRING);\n                        String collisionRule = wrapper.read(Types.STRING);\n\n                        int colour = wrapper.read(Types.VAR_INT);\n                        if (colour == 21) {\n                            colour = -1;\n                        }\n\n                        JsonElement prefixComponent = wrapper.read(Types.COMPONENT);\n                        JsonElement suffixComponent = wrapper.read(Types.COMPONENT);\n\n                        String prefix = protocol.jsonToLegacy(wrapper.user(), prefixComponent);\n                        if (ViaBackwards.getConfig().addTeamColorTo1_13Prefix()) {\n                            prefix += \"§\" + (colour > -1 && colour <= 15 ? Integer.toHexString(colour) : \"r\");\n                        }\n                        String suffix = protocol.jsonToLegacy(wrapper.user(), suffixComponent);\n\n                        wrapper.write(Types.STRING, ChatUtil.fromLegacyPrefix(prefix, 'f', 16));\n                        wrapper.write(Types.STRING, ChatUtil.fromLegacy(suffix, '\\0', 16));\n\n                        wrapper.write(Types.BYTE, flags);\n                        wrapper.write(Types.STRING, nameTagVisibility);\n                        wrapper.write(Types.STRING, collisionRule);\n\n                        wrapper.write(Types.BYTE, (byte) colour);\n                    }\n\n                    if (action == 0 || action == 3 || action == 4) {\n                        wrapper.passthrough(Types.STRING_ARRAY); //Entities\n                    }\n                });\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_13.COMMANDS, null, wrapper -> {\n            wrapper.cancel();\n\n            TabCompleteStorage storage = wrapper.user().get(TabCompleteStorage.class);\n\n            if (!storage.commands().isEmpty()) {\n                storage.commands().clear();\n            }\n\n            int size = wrapper.read(Types.VAR_INT);\n            boolean initialNodes = true;\n            for (int i = 0; i < size; i++) {\n                byte flags = wrapper.read(Types.BYTE);\n                wrapper.read(Types.VAR_INT_ARRAY_PRIMITIVE); // Children indices\n                if ((flags & 0x08) != 0) {\n                    wrapper.read(Types.VAR_INT); // Redirect node index\n                }\n\n                byte nodeType = (byte) (flags & 0x03);\n                if (initialNodes && nodeType == 2) {\n                    initialNodes = false;\n                }\n\n                if (nodeType == 1 || nodeType == 2) { // Literal/argument node\n                    String name = wrapper.read(Types.STRING);\n                    if (nodeType == 1 && initialNodes) {\n                        storage.commands().add('/' + name);\n                    }\n                }\n\n                if (nodeType == 2) { // Argument node\n                    commandRewriter.handleArgument(wrapper, wrapper.read(Types.STRING));\n                }\n\n                if ((flags & 0x10) != 0) {\n                    wrapper.read(Types.STRING); // Suggestion type\n                }\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_13.COMMAND_SUGGESTIONS, wrapper -> {\n            TabCompleteStorage storage = wrapper.user().get(TabCompleteStorage.class);\n            if (storage.lastRequest() == null) {\n                wrapper.cancel();\n                return;\n            }\n            if (storage.lastId() != wrapper.read(Types.VAR_INT)) wrapper.cancel();\n            int start = wrapper.read(Types.VAR_INT);\n            int length = wrapper.read(Types.VAR_INT);\n\n            int lastRequestPartIndex = storage.lastRequest().lastIndexOf(' ') + 1;\n            if (lastRequestPartIndex != start) wrapper.cancel(); // Client only replaces after space\n\n            if (length != storage.lastRequest().length() - lastRequestPartIndex) {\n                wrapper.cancel(); // We can't set the length in previous versions\n            }\n\n            int count = wrapper.passthrough(Types.VAR_INT);\n            for (int i = 0; i < count; i++) {\n                String match = wrapper.read(Types.STRING);\n                wrapper.write(Types.STRING, (start == 0 && !storage.isLastAssumeCommand() ? \"/\" : \"\") + match);\n                wrapper.read(Types.OPTIONAL_COMPONENT); // Remove tooltip\n            }\n        });\n\n        protocol.registerServerbound(ServerboundPackets1_12_1.COMMAND_SUGGESTION, wrapper -> {\n            TabCompleteStorage storage = wrapper.user().get(TabCompleteStorage.class);\n            List<String> suggestions = new ArrayList<>();\n\n            String command = wrapper.read(Types.STRING);\n            boolean assumeCommand = wrapper.read(Types.BOOLEAN);\n            wrapper.read(Types.OPTIONAL_POSITION1_8);\n\n            if (!assumeCommand && !command.startsWith(\"/\")) {\n                // Complete usernames for non-commands\n                String buffer = command.substring(command.lastIndexOf(' ') + 1);\n                for (String value : storage.usernames().values()) {\n                    if (startsWithIgnoreCase(value, buffer)) {\n                        suggestions.add(value);\n                    }\n                }\n            } else if (!storage.commands().isEmpty() && !command.contains(\" \")) {\n                // Complete commands names with values from 'Declare Commands' packet\n                for (String value : storage.commands()) {\n                    if (startsWithIgnoreCase(value, command)) {\n                        suggestions.add(value);\n                    }\n                }\n            }\n\n            if (!suggestions.isEmpty()) {\n                wrapper.cancel();\n                PacketWrapper response = wrapper.create(ClientboundPackets1_12_1.COMMAND_SUGGESTIONS);\n                response.write(Types.VAR_INT, suggestions.size());\n                for (String value : suggestions) {\n                    response.write(Types.STRING, value);\n                }\n                response.scheduleSend(Protocol1_13To1_12_2.class);\n                storage.setLastRequest(null);\n                return;\n            }\n\n            if (!assumeCommand && command.startsWith(\"/\")) {\n                command = command.substring(1);\n            }\n\n            int id = ThreadLocalRandom.current().nextInt();\n            wrapper.write(Types.VAR_INT, id);\n            wrapper.write(Types.STRING, command);\n\n            storage.setLastId(id);\n            storage.setLastAssumeCommand(assumeCommand);\n            storage.setLastRequest(command);\n        });\n\n        protocol.registerServerbound(ServerboundPackets1_12_1.CUSTOM_PAYLOAD, wrapper -> {\n            String channel = wrapper.read(Types.STRING);\n            switch (channel) {\n                case \"MC|BSign\", \"MC|BEdit\" -> {\n                    wrapper.setPacketType(ServerboundPackets1_13.EDIT_BOOK);\n                    Item book = wrapper.read(Types.ITEM1_8);\n                    wrapper.write(Types.ITEM1_13, protocol.getItemRewriter().handleItemToServer(wrapper.user(), book));\n                    boolean signing = channel.equals(\"MC|BSign\");\n                    wrapper.write(Types.BOOLEAN, signing);\n                }\n                case \"MC|ItemName\" -> wrapper.setPacketType(ServerboundPackets1_13.RENAME_ITEM);\n                case \"MC|AdvCmd\" -> {\n                    byte type = wrapper.read(Types.BYTE);\n                    if (type == 0) {\n                        //Information from https://wiki.vg/index.php?title=Plugin_channels&oldid=14089 (see web archive)\n                        //The Vanilla client only uses this for command block minecarts and uses MC|AutoCmd for blocks, but the server still accepts it for either.\n                        //Maybe older versions used this and we need to implement this? The issue is that we would have to save the command block types\n                        wrapper.setPacketType(ServerboundPackets1_13.SET_COMMAND_BLOCK);\n                        wrapper.cancel();\n                        if (Via.getConfig().logOtherConversionWarnings()) {\n                            protocol.getLogger().warning(\"Client send MC|AdvCmd custom payload to update command block, weird!\");\n                        }\n                    } else if (type == 1) {\n                        wrapper.setPacketType(ServerboundPackets1_13.SET_COMMAND_MINECART);\n                        wrapper.write(Types.VAR_INT, wrapper.read(Types.INT)); //Entity Id\n                        wrapper.passthrough(Types.STRING); //Command\n                        wrapper.passthrough(Types.BOOLEAN); //Track Output\n\n                    } else {\n                        wrapper.cancel();\n                    }\n                }\n                case \"MC|AutoCmd\" -> {\n                    wrapper.setPacketType(ServerboundPackets1_13.SET_COMMAND_BLOCK);\n\n                    int x = wrapper.read(Types.INT);\n                    int y = wrapper.read(Types.INT);\n                    int z = wrapper.read(Types.INT);\n\n                    wrapper.write(Types.BLOCK_POSITION1_8, new BlockPosition(x, (short) y, z));\n\n                    wrapper.passthrough(Types.STRING);  //Command\n\n                    byte flags = 0;\n                    if (wrapper.read(Types.BOOLEAN)) flags |= 0x01; //Track Output\n\n                    String mode = wrapper.read(Types.STRING);\n\n                    int modeId = mode.equals(\"SEQUENCE\") ? 0 : mode.equals(\"AUTO\") ? 1 : 2;\n                    wrapper.write(Types.VAR_INT, modeId);\n\n                    if (wrapper.read(Types.BOOLEAN)) flags |= 0x02; //Is conditional\n                    if (wrapper.read(Types.BOOLEAN)) flags |= 0x04; //Automatic\n\n                    wrapper.write(Types.BYTE, flags);\n                }\n                case \"MC|Struct\" -> {\n                    wrapper.setPacketType(ServerboundPackets1_13.SET_STRUCTURE_BLOCK);\n                    int x = wrapper.read(Types.INT);\n                    int y = wrapper.read(Types.INT);\n                    int z = wrapper.read(Types.INT);\n                    wrapper.write(Types.BLOCK_POSITION1_8, new BlockPosition(x, (short) y, z));\n                    wrapper.write(Types.VAR_INT, wrapper.read(Types.BYTE) - 1);\n                    String mode = wrapper.read(Types.STRING);\n                    int modeId = mode.equals(\"SAVE\") ? 0 : mode.equals(\"LOAD\") ? 1 : mode.equals(\"CORNER\") ? 2 : 3;\n                    wrapper.write(Types.VAR_INT, modeId);\n                    wrapper.passthrough(Types.STRING); //Name\n\n                    wrapper.write(Types.BYTE, wrapper.read(Types.INT).byteValue()); //Offset X\n\n                    wrapper.write(Types.BYTE, wrapper.read(Types.INT).byteValue()); //Offset Y\n\n                    wrapper.write(Types.BYTE, wrapper.read(Types.INT).byteValue()); //Offset Z\n\n                    wrapper.write(Types.BYTE, wrapper.read(Types.INT).byteValue()); //Size X\n\n                    wrapper.write(Types.BYTE, wrapper.read(Types.INT).byteValue()); //Size Y\n\n                    wrapper.write(Types.BYTE, wrapper.read(Types.INT).byteValue()); //Size Z\n\n                    String mirror = wrapper.read(Types.STRING);\n                    int mirrorId = mode.equals(\"NONE\") ? 0 : mode.equals(\"LEFT_RIGHT\") ? 1 : 2;\n                    String rotation = wrapper.read(Types.STRING);\n                    int rotationId = mode.equals(\"NONE\") ? 0 : mode.equals(\"CLOCKWISE_90\") ? 1 : mode.equals(\"CLOCKWISE_180\") ? 2 : 3;\n                    wrapper.passthrough(Types.STRING); //Metadata\n\n                    byte flags = 0;\n                    if (wrapper.read(Types.BOOLEAN)) flags |= 0x01; //Ignore entities\n                    if (wrapper.read(Types.BOOLEAN)) flags |= 0x02; //Show air\n                    if (wrapper.read(Types.BOOLEAN)) flags |= 0x04; //Show bounding box\n                    wrapper.passthrough(Types.FLOAT); //Integrity\n\n                    wrapper.passthrough(Types.VAR_LONG); //Seed\n\n                    wrapper.write(Types.BYTE, flags);\n                }\n                case \"MC|Beacon\" -> {\n                    wrapper.setPacketType(ServerboundPackets1_13.SET_BEACON);\n                    wrapper.write(Types.VAR_INT, wrapper.read(Types.INT)); //Primary Effect\n                    wrapper.write(Types.VAR_INT, wrapper.read(Types.INT)); //Secondary Effect\n                }\n                case \"MC|TrSel\" -> {\n                    wrapper.setPacketType(ServerboundPackets1_13.SELECT_TRADE);\n                    wrapper.write(Types.VAR_INT, wrapper.read(Types.INT)); //Slot\n                }\n                case \"MC|PickItem\" -> wrapper.setPacketType(ServerboundPackets1_13.PICK_ITEM);\n                default -> {\n                    String newChannel = ItemPacketRewriter1_13.getNewPluginChannelId(channel);\n                    if (newChannel == null) {\n                        if (Via.getConfig().logOtherConversionWarnings()) {\n                            protocol.getLogger().warning(\"Ignoring serverbound plugin message with channel: \" + channel);\n                        }\n                        wrapper.cancel();\n                        return;\n                    }\n                    wrapper.write(Types.STRING, newChannel);\n\n                    if (newChannel.equals(\"minecraft:register\") || newChannel.equals(\"minecraft:unregister\")) {\n                        String[] channels = new String(wrapper.read(Types.SERVERBOUND_CUSTOM_PAYLOAD_DATA), StandardCharsets.UTF_8).split(\"\\0\");\n                        List<String> rewrittenChannels = new ArrayList<>();\n                        for (String s : channels) {\n                            String rewritten = ItemPacketRewriter1_13.getNewPluginChannelId(s);\n                            if (rewritten != null) {\n                                rewrittenChannels.add(rewritten);\n                            } else if (Via.getConfig().logOtherConversionWarnings()) {\n                                protocol.getLogger().warning(\"Ignoring plugin channel in serverbound \" + Key.stripMinecraftNamespace(newChannel).toUpperCase(Locale.ROOT) + \": \" + s);\n                            }\n                        }\n                        if (!rewrittenChannels.isEmpty()) {\n                            wrapper.write(Types.SERVERBOUND_CUSTOM_PAYLOAD_DATA, Joiner.on('\\0').join(rewrittenChannels).getBytes(StandardCharsets.UTF_8));\n                        } else {\n                            wrapper.cancel();\n                        }\n                    }\n                }\n            }\n        });\n\n        protocol.replaceClientbound(ClientboundPackets1_13.AWARD_STATS, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT);\n                handler(wrapper -> {\n                    int size = wrapper.get(Types.VAR_INT, 0);\n                    int newSize = size;\n                    for (int i = 0; i < size; i++) {\n                        int categoryId = wrapper.read(Types.VAR_INT);\n                        int statisticId = wrapper.read(Types.VAR_INT);\n\n                        String name = \"\";\n                        // categories 0-7 (items, blocks, entities) - probably not feasible\n                        switch (categoryId) {\n                            case 0, 1, 2, 3, 4, 5, 6, 7 -> {\n                                wrapper.read(Types.VAR_INT); // remove value\n                                newSize--;\n                                continue;\n                            }\n                            case 8 -> {\n                                name = protocol.getMappingData().getStatisticMappings().get(statisticId);\n                                if (name == null) {\n                                    wrapper.read(Types.VAR_INT);\n                                    newSize--;\n                                    continue;\n                                }\n                            }\n                        }\n\n                        wrapper.write(Types.STRING, name); // string id\n                        wrapper.passthrough(Types.VAR_INT); // value\n                    }\n\n                    if (newSize != size) {\n                        wrapper.set(Types.VAR_INT, 0, newSize);\n                    }\n                });\n            }\n        });\n    }\n\n    private static boolean startsWithIgnoreCase(String string, String prefix) {\n        if (string.length() < prefix.length()) {\n            return false;\n        }\n        return string.regionMatches(true, 0, prefix, 0, prefix.length());\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_13to1_12_2/rewriter/SoundPacketRewriter1_13.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_13to1_12_2.rewriter;\n\nimport com.viaversion.viabackwards.protocol.v1_13to1_12_2.Protocol1_13To1_12_2;\nimport com.viaversion.viabackwards.protocol.v1_13to1_12_2.data.NamedSoundMappings1_12_2;\nimport com.viaversion.viaversion.api.rewriter.RewriterBase;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.protocols.v1_12_2to1_13.packet.ClientboundPackets1_13;\nimport com.viaversion.viaversion.protocols.v1_12to1_12_1.packet.ClientboundPackets1_12_1;\n\npublic class SoundPacketRewriter1_13 extends RewriterBase<Protocol1_13To1_12_2> {\n    private static final String[] SOUND_SOURCES = {\"master\", \"music\", \"record\", \"weather\", \"block\", \"hostile\", \"neutral\", \"player\", \"ambient\", \"voice\"};\n\n    public SoundPacketRewriter1_13(Protocol1_13To1_12_2 protocol) {\n        super(protocol);\n    }\n\n    @Override\n    protected void registerPackets() {\n        protocol.replaceClientbound(ClientboundPackets1_13.CUSTOM_SOUND, wrapper -> {\n            String sound = wrapper.read(Types.STRING);\n            String mappedSound = NamedSoundMappings1_12_2.getOldId(sound);\n            if (mappedSound != null || (mappedSound = protocol.getMappingData().getMappedNamedSound(sound)) != null) {\n                wrapper.write(Types.STRING, mappedSound);\n            } else {\n                wrapper.write(Types.STRING, sound);\n            }\n        });\n\n        // Stop Sound -> Plugin Message\n        protocol.registerClientbound(ClientboundPackets1_13.STOP_SOUND, ClientboundPackets1_12_1.CUSTOM_PAYLOAD, wrapper -> {\n            wrapper.write(Types.STRING, \"MC|StopSound\");\n            byte flags = wrapper.read(Types.BYTE);\n            String source;\n            if ((flags & 0x01) != 0) {\n                source = SOUND_SOURCES[wrapper.read(Types.VAR_INT)];\n            } else {\n                source = \"\";\n            }\n\n            String sound;\n            if ((flags & 0x02) != 0) {\n                String newSound = wrapper.read(Types.STRING);\n                sound = protocol.getMappingData().getMappedNamedSound(newSound);\n                if (sound == null) {\n                    sound = \"\";\n                }\n            } else {\n                sound = \"\";\n            }\n\n            wrapper.write(Types.STRING, source);\n            wrapper.write(Types.STRING, sound);\n        });\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_13to1_12_2/storage/BackwardsBlockStorage.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.viaversion.viabackwards.protocol.v1_13to1_12_2.storage;\n\nimport com.viaversion.viaversion.api.connection.StorableObject;\nimport com.viaversion.viaversion.api.minecraft.BlockPosition;\nimport com.viaversion.viaversion.libs.fastutil.ints.IntOpenHashSet;\nimport com.viaversion.viaversion.libs.fastutil.ints.IntSet;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\nimport org.checkerframework.checker.nullness.qual.Nullable;\n\npublic class BackwardsBlockStorage implements StorableObject {\n    // This BlockStorage is very exclusive (;\n    private static final IntSet WHITELIST = new IntOpenHashSet(779);\n\n    static {\n        // Flower pots\n        for (int i = 5265; i <= 5286; i++) {\n            WHITELIST.add(i);\n        }\n\n        // Add those beds\n        for (int i = 0; i < (16 * 16); i++) {\n            WHITELIST.add(748 + i);\n        }\n\n        // Add the banners\n        for (int i = 6854; i <= 7173; i++) {\n            WHITELIST.add(i);\n        }\n\n        // Spawner\n        WHITELIST.add(1647);\n\n        // Skulls\n        for (int i = 5447; i <= 5566; i++) {\n            WHITELIST.add(i);\n        }\n\n        // pistons\n        for (int i = 1028; i <= 1039; i++) {\n            WHITELIST.add(i);\n        }\n        for (int i = 1047; i <= 1082; i++) {\n            WHITELIST.add(i);\n        }\n        for (int i = 1099; i <= 1110; i++) {\n            WHITELIST.add(i);\n        }\n    }\n\n    private final Map<BlockPosition, Integer> blocks = new ConcurrentHashMap<>();\n\n    public void checkAndStore(BlockPosition position, int block) {\n        if (!WHITELIST.contains(block)) {\n            // Remove if not whitelisted\n            blocks.remove(position);\n            return;\n        }\n\n        blocks.put(position, block);\n    }\n\n    public @Nullable Integer get(BlockPosition position) {\n        return blocks.get(position);\n    }\n\n    public int remove(BlockPosition position) {\n        return blocks.remove(position);\n    }\n\n    public void clear() {\n        blocks.clear();\n    }\n\n    public Map<BlockPosition, Integer> getBlocks() {\n        return blocks;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_13to1_12_2/storage/NoteBlockStorage.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_13to1_12_2.storage;\n\nimport com.viaversion.viaversion.api.connection.StorableObject;\nimport com.viaversion.viaversion.api.minecraft.BlockPosition;\nimport com.viaversion.viaversion.libs.fastutil.objects.Object2IntMap;\nimport com.viaversion.viaversion.libs.fastutil.objects.Object2IntOpenHashMap;\nimport com.viaversion.viaversion.util.Pair;\n\npublic class NoteBlockStorage implements StorableObject {\n\n    private static final int MAX_NOTE_ID = 24;\n\n    private final Object2IntMap<BlockPosition> noteBlockUpdates = new Object2IntOpenHashMap<>();\n\n    public void storeNoteBlockUpdate(final BlockPosition position, final int blockStateId) {\n        noteBlockUpdates.put(position, blockStateId);\n    }\n\n    public Pair<Integer, Integer> getNoteBlockUpdate(final BlockPosition position) {\n        if (!noteBlockUpdates.containsKey(position)) {\n            return null;\n        }\n        int relativeBlockState = noteBlockUpdates.removeInt(position) - 249;\n        relativeBlockState = relativeBlockState / 2; // Get rid of powered state\n\n        return new Pair<>(relativeBlockState / MAX_NOTE_ID + 1, relativeBlockState % MAX_NOTE_ID + 1);\n    }\n\n    public void clear() {\n        noteBlockUpdates.clear();\n    }\n\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_13to1_12_2/storage/PlayerPositionStorage1_13.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_13to1_12_2.storage;\n\nimport com.viaversion.viabackwards.api.entities.storage.PlayerPositionStorage;\n\npublic class PlayerPositionStorage1_13 extends PlayerPositionStorage {\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_13to1_12_2/storage/TabCompleteStorage.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_13to1_12_2.storage;\n\nimport com.viaversion.viaversion.api.connection.StorableObject;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.UUID;\n\npublic class TabCompleteStorage implements StorableObject {\n    private final Map<UUID, String> usernames = new HashMap<>();\n    private final Set<String> commands = new HashSet<>();\n    private int lastId;\n    private String lastRequest;\n    private boolean lastAssumeCommand;\n\n    public Map<UUID, String> usernames() {\n        return usernames;\n    }\n\n    public Set<String> commands() {\n        return commands;\n    }\n\n    public int lastId() {\n        return lastId;\n    }\n\n    public void setLastId(final int lastId) {\n        this.lastId = lastId;\n    }\n\n    public String lastRequest() {\n        return lastRequest;\n    }\n\n    public void setLastRequest(String lastRequest) {\n        this.lastRequest = lastRequest;\n    }\n\n    public boolean isLastAssumeCommand() {\n        return lastAssumeCommand;\n    }\n\n    public void setLastAssumeCommand(boolean lastAssumeCommand) {\n        this.lastAssumeCommand = lastAssumeCommand;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_14_1to1_14/Protocol1_14_1To1_14.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_14_1to1_14;\n\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viabackwards.protocol.v1_14_1to1_14.rewriter.EntityPacketRewriter1_14_1;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.ClientWorld;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_15;\nimport com.viaversion.viaversion.data.entity.EntityTrackerBase;\nimport com.viaversion.viaversion.protocols.v1_13_2to1_14.packet.ClientboundPackets1_14;\nimport com.viaversion.viaversion.protocols.v1_13_2to1_14.packet.ServerboundPackets1_14;\n\npublic class Protocol1_14_1To1_14 extends BackwardsProtocol<ClientboundPackets1_14, ClientboundPackets1_14, ServerboundPackets1_14, ServerboundPackets1_14> {\n\n    private final EntityPacketRewriter1_14_1 entityRewriter = new EntityPacketRewriter1_14_1(this);\n\n    public Protocol1_14_1To1_14() {\n        super(ClientboundPackets1_14.class, ClientboundPackets1_14.class, ServerboundPackets1_14.class, ServerboundPackets1_14.class);\n    }\n\n    @Override\n    protected void registerPackets() {\n        entityRewriter.register();\n    }\n\n    @Override\n    public void init(UserConnection user) {\n        user.addEntityTracker(this.getClass(), new EntityTrackerBase(user, EntityTypes1_15.PLAYER));\n        user.addClientWorld(this.getClass(), new ClientWorld());\n    }\n\n    @Override\n    public EntityPacketRewriter1_14_1 getEntityRewriter() {\n        return entityRewriter;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_14_1to1_14/rewriter/EntityPacketRewriter1_14_1.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_14_1to1_14.rewriter;\n\nimport com.viaversion.viabackwards.api.rewriters.LegacyEntityRewriter;\nimport com.viaversion.viabackwards.protocol.v1_14_1to1_14.Protocol1_14_1To1_14;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityType;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_14;\nimport com.viaversion.viaversion.api.minecraft.entitydata.EntityData;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.version.Types1_14;\nimport com.viaversion.viaversion.protocols.v1_13_2to1_14.packet.ClientboundPackets1_14;\nimport java.util.List;\n\npublic class EntityPacketRewriter1_14_1 extends LegacyEntityRewriter<ClientboundPackets1_14, Protocol1_14_1To1_14> {\n\n    public EntityPacketRewriter1_14_1(Protocol1_14_1To1_14 protocol) {\n        super(protocol);\n    }\n\n    @Override\n    protected void registerPackets() {\n        registerTracker(ClientboundPackets1_14.ADD_EXPERIENCE_ORB, EntityTypes1_14.EXPERIENCE_ORB);\n        registerTracker(ClientboundPackets1_14.ADD_GLOBAL_ENTITY, EntityTypes1_14.LIGHTNING_BOLT);\n        registerTracker(ClientboundPackets1_14.ADD_PAINTING, EntityTypes1_14.PAINTING);\n        registerTracker(ClientboundPackets1_14.ADD_PLAYER, EntityTypes1_14.PLAYER);\n        registerJoinGame(ClientboundPackets1_14.LOGIN, EntityTypes1_14.PLAYER);\n        registerRespawn(ClientboundPackets1_14.RESPAWN);\n\n        protocol.registerClientbound(ClientboundPackets1_14.ADD_ENTITY, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT); // 0 - Entity id\n                map(Types.UUID); // 1 - UUID\n                map(Types.VAR_INT); // 2 - Type\n\n                handler(getTrackerHandler());\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_14.ADD_MOB, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT); // 0 - Entity ID\n                map(Types.UUID); // 1 - Entity UUID\n                map(Types.VAR_INT); // 2 - Entity Type\n                map(Types.DOUBLE); // 3 - X\n                map(Types.DOUBLE); // 4 - Y\n                map(Types.DOUBLE); // 5 - Z\n                map(Types.BYTE); // 6 - Yaw\n                map(Types.BYTE); // 7 - Pitch\n                map(Types.BYTE); // 8 - Head Pitch\n                map(Types.SHORT); // 9 - Velocity X\n                map(Types.SHORT); // 10 - Velocity Y\n                map(Types.SHORT); // 11 - Velocity Z\n                map(Types1_14.ENTITY_DATA_LIST); // 12 - Entity data\n\n                handler(wrapper -> {\n                    int entityId = wrapper.get(Types.VAR_INT, 0);\n                    int type = wrapper.get(Types.VAR_INT, 1);\n\n                    // Register Type ID\n                    tracker(wrapper.user()).addEntity(entityId, EntityTypes1_14.getTypeFromId(type));\n\n                    List<EntityData> entityDataList = wrapper.get(Types1_14.ENTITY_DATA_LIST, 0);\n                    handleEntityData(entityId, entityDataList, wrapper.user());\n                });\n            }\n        });\n\n        // Entity data\n        registerSetEntityData(ClientboundPackets1_14.SET_ENTITY_DATA, Types1_14.ENTITY_DATA_LIST);\n    }\n\n    @Override\n    protected void registerRewrites() {\n        filter().type(EntityTypes1_14.VILLAGER).cancel(15);\n        filter().type(EntityTypes1_14.VILLAGER).index(16).toIndex(15);\n        filter().type(EntityTypes1_14.WANDERING_TRADER).cancel(15);\n    }\n\n    @Override\n    public EntityType typeFromId(int typeId) {\n        return EntityTypes1_14.getTypeFromId(typeId);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_14_2to1_14_1/Protocol1_14_2To1_14_1.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_14_2to1_14_1;\n\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viaversion.protocols.v1_13_2to1_14.packet.ClientboundPackets1_14;\nimport com.viaversion.viaversion.protocols.v1_13_2to1_14.packet.ServerboundPackets1_14;\n\npublic class Protocol1_14_2To1_14_1 extends BackwardsProtocol<ClientboundPackets1_14, ClientboundPackets1_14, ServerboundPackets1_14, ServerboundPackets1_14> {\n\n    public Protocol1_14_2To1_14_1() {\n        super(ClientboundPackets1_14.class, ClientboundPackets1_14.class, ServerboundPackets1_14.class, ServerboundPackets1_14.class);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_14_3to1_14_2/Protocol1_14_3To1_14_2.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_14_3to1_14_2;\n\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.protocols.v1_13_2to1_14.packet.ClientboundPackets1_14;\nimport com.viaversion.viaversion.protocols.v1_13_2to1_14.packet.ServerboundPackets1_14;\nimport com.viaversion.viaversion.rewriter.RecipeRewriter;\nimport com.viaversion.viaversion.util.Key;\n\npublic class Protocol1_14_3To1_14_2 extends BackwardsProtocol<ClientboundPackets1_14, ClientboundPackets1_14, ServerboundPackets1_14, ServerboundPackets1_14> {\n\n    public Protocol1_14_3To1_14_2() {\n        super(ClientboundPackets1_14.class, ClientboundPackets1_14.class, ServerboundPackets1_14.class, ServerboundPackets1_14.class);\n    }\n\n    @Override\n    protected void registerPackets() {\n        registerClientbound(ClientboundPackets1_14.MERCHANT_OFFERS, wrapper -> {\n            wrapper.passthrough(Types.VAR_INT);\n            int size = wrapper.passthrough(Types.UNSIGNED_BYTE);\n            for (int i = 0; i < size; i++) {\n                wrapper.passthrough(Types.ITEM1_13_2);\n                wrapper.passthrough(Types.ITEM1_13_2);\n                if (wrapper.passthrough(Types.BOOLEAN)) {\n                    wrapper.passthrough(Types.ITEM1_13_2);\n                }\n                wrapper.passthrough(Types.BOOLEAN);\n                wrapper.passthrough(Types.INT);\n                wrapper.passthrough(Types.INT);\n                wrapper.passthrough(Types.INT);\n                wrapper.passthrough(Types.INT);\n                wrapper.passthrough(Types.FLOAT);\n            }\n            wrapper.passthrough(Types.VAR_INT);\n            wrapper.passthrough(Types.VAR_INT);\n\n            wrapper.passthrough(Types.BOOLEAN);\n            wrapper.read(Types.BOOLEAN);\n        });\n\n        RecipeRewriter<ClientboundPackets1_14> recipeHandler = new RecipeRewriter<>(this);\n        registerClientbound(ClientboundPackets1_14.UPDATE_RECIPES, wrapper -> {\n            int size = wrapper.passthrough(Types.VAR_INT);\n            int deleted = 0;\n            for (int i = 0; i < size; i++) {\n                String fullType = wrapper.read(Types.STRING);\n                String type = Key.stripMinecraftNamespace(fullType);\n                String id = wrapper.read(Types.STRING); // id\n\n                if (type.equals(\"crafting_special_repairitem\")) {\n                    deleted++;\n                    continue;\n                }\n\n                wrapper.write(Types.STRING, fullType);\n                wrapper.write(Types.STRING, id);\n\n                recipeHandler.handleRecipeType(wrapper, type);\n            }\n\n            wrapper.set(Types.VAR_INT, 0, size - deleted);\n        });\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_14_4to1_14_3/Protocol1_14_4To1_14_3.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_14_4to1_14_3;\n\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.protocols.v1_13_2to1_14.packet.ClientboundPackets1_14;\nimport com.viaversion.viaversion.protocols.v1_13_2to1_14.packet.ServerboundPackets1_14;\nimport com.viaversion.viaversion.protocols.v1_14_3to1_14_4.packet.ClientboundPackets1_14_4;\n\npublic class Protocol1_14_4To1_14_3 extends BackwardsProtocol<ClientboundPackets1_14_4, ClientboundPackets1_14, ServerboundPackets1_14, ServerboundPackets1_14> {\n\n    public Protocol1_14_4To1_14_3() {\n        super(ClientboundPackets1_14_4.class, ClientboundPackets1_14.class, ServerboundPackets1_14.class, ServerboundPackets1_14.class);\n    }\n\n    @Override\n    protected void registerPackets() {\n        // Acknowledge Player Digging - added in pre4\n        registerClientbound(ClientboundPackets1_14_4.BLOCK_BREAK_ACK, ClientboundPackets1_14.BLOCK_UPDATE, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.BLOCK_POSITION1_14);\n                map(Types.VAR_INT);\n                handler(wrapper -> {\n                    int status = wrapper.read(Types.VAR_INT);\n                    boolean allGood = wrapper.read(Types.BOOLEAN);\n                    if (allGood && status == 0) {\n                        wrapper.cancel();\n                    }\n                });\n            }\n        });\n\n        registerClientbound(ClientboundPackets1_14_4.MERCHANT_OFFERS, wrapper -> {\n            wrapper.passthrough(Types.VAR_INT);\n            int size = wrapper.passthrough(Types.UNSIGNED_BYTE);\n            for (int i = 0; i < size; i++) {\n                wrapper.passthrough(Types.ITEM1_13_2);\n                wrapper.passthrough(Types.ITEM1_13_2);\n                if (wrapper.passthrough(Types.BOOLEAN)) {\n                    wrapper.passthrough(Types.ITEM1_13_2);\n                }\n                wrapper.passthrough(Types.BOOLEAN);\n                wrapper.passthrough(Types.INT);\n                wrapper.passthrough(Types.INT);\n                wrapper.passthrough(Types.INT);\n                wrapper.passthrough(Types.INT);\n                wrapper.passthrough(Types.FLOAT);\n                wrapper.read(Types.INT); // demand value added in pre-5\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_14to1_13_2/Protocol1_14To1_13_2.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_14to1_13_2;\n\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viabackwards.api.data.BackwardsMappingData;\nimport com.viaversion.viabackwards.api.rewriters.text.JsonNBTComponentRewriter;\nimport com.viaversion.viabackwards.protocol.v1_14to1_13_2.data.BackwardsMappingData1_14;\nimport com.viaversion.viabackwards.protocol.v1_14to1_13_2.rewriter.BlockItemPacketRewriter1_14;\nimport com.viaversion.viabackwards.protocol.v1_14to1_13_2.rewriter.CommandRewriter1_14;\nimport com.viaversion.viabackwards.protocol.v1_14to1_13_2.rewriter.EntityPacketRewriter1_14;\nimport com.viaversion.viabackwards.protocol.v1_14to1_13_2.rewriter.PlayerPacketRewriter1_14;\nimport com.viaversion.viabackwards.protocol.v1_14to1_13_2.rewriter.SoundPacketRewriter1_14;\nimport com.viaversion.viabackwards.protocol.v1_14to1_13_2.storage.ChunkLightStorage;\nimport com.viaversion.viabackwards.protocol.v1_14to1_13_2.storage.DifficultyStorage;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.ClientWorld;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_14;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.data.entity.EntityTrackerBase;\nimport com.viaversion.viaversion.protocols.v1_12_2to1_13.packet.ClientboundPackets1_13;\nimport com.viaversion.viaversion.protocols.v1_12_2to1_13.packet.ServerboundPackets1_13;\nimport com.viaversion.viaversion.protocols.v1_13_2to1_14.packet.ClientboundPackets1_14;\nimport com.viaversion.viaversion.protocols.v1_13_2to1_14.packet.ServerboundPackets1_14;\nimport com.viaversion.viaversion.rewriter.BlockRewriter;\nimport com.viaversion.viaversion.rewriter.ParticleRewriter;\nimport com.viaversion.viaversion.rewriter.text.ComponentRewriterBase;\n\npublic class Protocol1_14To1_13_2 extends BackwardsProtocol<ClientboundPackets1_14, ClientboundPackets1_13, ServerboundPackets1_14, ServerboundPackets1_13> {\n\n    public static final BackwardsMappingData1_14 MAPPINGS = new BackwardsMappingData1_14();\n    private final EntityPacketRewriter1_14 entityRewriter = new EntityPacketRewriter1_14(this);\n    private final BlockItemPacketRewriter1_14 itemRewriter = new BlockItemPacketRewriter1_14(this);\n    private final BlockRewriter<ClientboundPackets1_14> blockRewriter = BlockRewriter.legacy(this);\n    private final ParticleRewriter<ClientboundPackets1_14> particleRewriter = new ParticleRewriter<>(this);\n    private final JsonNBTComponentRewriter<ClientboundPackets1_14> translatableRewriter = new JsonNBTComponentRewriter<>(this, ComponentRewriterBase.ReadType.JSON);\n\n    public Protocol1_14To1_13_2() {\n        super(ClientboundPackets1_14.class, ClientboundPackets1_13.class, ServerboundPackets1_14.class, ServerboundPackets1_13.class);\n    }\n\n    @Override\n    protected void registerPackets() {\n        super.registerPackets();\n\n        new CommandRewriter1_14(this).registerDeclareCommands(ClientboundPackets1_14.COMMANDS);\n        new PlayerPacketRewriter1_14(this).register();\n        new SoundPacketRewriter1_14(this).register();\n\n        cancelClientbound(ClientboundPackets1_14.SET_CHUNK_CACHE_CENTER);\n        cancelClientbound(ClientboundPackets1_14.SET_CHUNK_CACHE_RADIUS);\n\n        registerClientbound(ClientboundPackets1_14.UPDATE_TAGS, wrapper -> {\n            int blockTagsSize = wrapper.passthrough(Types.VAR_INT);\n            for (int i = 0; i < blockTagsSize; i++) {\n                wrapper.passthrough(Types.STRING);\n                int[] blockIds = wrapper.passthrough(Types.VAR_INT_ARRAY_PRIMITIVE);\n                for (int j = 0; j < blockIds.length; j++) {\n                    int id = blockIds[j];\n                    // Ignore new blocktags\n                    int blockId = MAPPINGS.getNewBlockId(id);\n                    blockIds[j] = blockId;\n                }\n            }\n\n            int itemTagsSize = wrapper.passthrough(Types.VAR_INT);\n            for (int i = 0; i < itemTagsSize; i++) {\n                wrapper.passthrough(Types.STRING);\n                int[] itemIds = wrapper.passthrough(Types.VAR_INT_ARRAY_PRIMITIVE);\n                for (int j = 0; j < itemIds.length; j++) {\n                    int itemId = itemIds[j];\n                    // Ignore new itemtags\n                    int oldId = MAPPINGS.getItemMappings().getNewId(itemId);\n                    itemIds[j] = oldId;\n                }\n            }\n\n            int fluidTagsSize = wrapper.passthrough(Types.VAR_INT); // fluid tags\n            for (int i = 0; i < fluidTagsSize; i++) {\n                wrapper.passthrough(Types.STRING);\n                wrapper.passthrough(Types.VAR_INT_ARRAY_PRIMITIVE);\n            }\n\n            // Eat entity tags\n            int entityTagsSize = wrapper.read(Types.VAR_INT);\n            for (int i = 0; i < entityTagsSize; i++) {\n                wrapper.read(Types.STRING);\n                wrapper.read(Types.VAR_INT_ARRAY_PRIMITIVE);\n            }\n        });\n\n        registerClientbound(ClientboundPackets1_14.LIGHT_UPDATE, null, wrapper -> {\n            int x = wrapper.read(Types.VAR_INT);\n            int z = wrapper.read(Types.VAR_INT);\n            int skyLightMask = wrapper.read(Types.VAR_INT);\n            int blockLightMask = wrapper.read(Types.VAR_INT);\n            int emptySkyLightMask = wrapper.read(Types.VAR_INT);\n            int emptyBlockLightMask = wrapper.read(Types.VAR_INT);\n\n            byte[][] skyLight = new byte[16][];\n            // we don't need void and +256 light\n            if (isSet(skyLightMask, 0)) {\n                wrapper.read(Types.BYTE_ARRAY_PRIMITIVE);\n            }\n            for (int i = 0; i < 16; i++) {\n                if (isSet(skyLightMask, i + 1)) {\n                    skyLight[i] = wrapper.read(Types.BYTE_ARRAY_PRIMITIVE);\n                } else if (isSet(emptySkyLightMask, i + 1)) {\n                    skyLight[i] = ChunkLightStorage.EMPTY_LIGHT;\n                }\n            }\n            if (isSet(skyLightMask, 17)) {\n                wrapper.read(Types.BYTE_ARRAY_PRIMITIVE);\n            }\n\n            byte[][] blockLight = new byte[16][];\n            if (isSet(blockLightMask, 0)) {\n                wrapper.read(Types.BYTE_ARRAY_PRIMITIVE);\n            }\n            for (int i = 0; i < 16; i++) {\n                if (isSet(blockLightMask, i + 1)) {\n                    blockLight[i] = wrapper.read(Types.BYTE_ARRAY_PRIMITIVE);\n                } else if (isSet(emptyBlockLightMask, i + 1)) {\n                    blockLight[i] = ChunkLightStorage.EMPTY_LIGHT;\n                }\n            }\n            if (isSet(blockLightMask, 17)) {\n                wrapper.read(Types.BYTE_ARRAY_PRIMITIVE);\n            }\n\n            //TODO Soft memory leak: Don't store light if chunk is already loaded\n            wrapper.user().get(ChunkLightStorage.class).setStoredLight(skyLight, blockLight, x, z);\n            wrapper.cancel();\n        });\n    }\n\n    private static boolean isSet(int mask, int i) {\n        return (mask & (1 << i)) != 0;\n    }\n\n    @Override\n    public void init(UserConnection user) {\n        user.addEntityTracker(this.getClass(), new EntityTrackerBase(user, EntityTypes1_14.PLAYER));\n        user.addClientWorld(this.getClass(), new ClientWorld());\n\n        if (!user.has(ChunkLightStorage.class)) {\n            user.put(new ChunkLightStorage());\n        }\n\n        user.put(new DifficultyStorage());\n    }\n\n    @Override\n    public BackwardsMappingData getMappingData() {\n        return MAPPINGS;\n    }\n\n    @Override\n    public EntityPacketRewriter1_14 getEntityRewriter() {\n        return entityRewriter;\n    }\n\n    @Override\n    public BlockItemPacketRewriter1_14 getItemRewriter() {\n        return itemRewriter;\n    }\n\n    @Override\n    public BlockRewriter<ClientboundPackets1_14> getBlockRewriter() {\n        return blockRewriter;\n    }\n\n    @Override\n    public ParticleRewriter<ClientboundPackets1_14> getParticleRewriter() {\n        return particleRewriter;\n    }\n\n    @Override\n    public JsonNBTComponentRewriter<ClientboundPackets1_14> getComponentRewriter() {\n        return translatableRewriter;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_14to1_13_2/data/BackwardsMappingData1_14.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_14to1_13_2.data;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.viabackwards.ViaBackwards;\nimport com.viaversion.viabackwards.api.data.BackwardsMappingData;\nimport com.viaversion.viaversion.protocols.v1_13_2to1_14.Protocol1_13_2To1_14;\n\npublic final class BackwardsMappingData1_14 extends BackwardsMappingData {\n\n    public BackwardsMappingData1_14() {\n        super(\"1.14\", \"1.13.2\", Protocol1_13_2To1_14.class);\n    }\n\n    @Override\n    protected void loadExtras(final CompoundTag data) {\n        super.loadExtras(data);\n\n        if (ViaBackwards.getConfig().scaffoldingToWater()) {\n            for (int i = 11099; i <= 11130; i++) {\n                blockStateMappings.setNewId(i, 49);\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_14to1_13_2/rewriter/BlockItemPacketRewriter1_14.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_14to1_13_2.rewriter;\n\nimport com.google.common.collect.ImmutableSet;\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.ListTag;\nimport com.viaversion.nbt.tag.StringTag;\nimport com.viaversion.viabackwards.api.data.TranslatableMappings;\nimport com.viaversion.viabackwards.api.rewriters.BackwardsItemRewriter;\nimport com.viaversion.viabackwards.api.rewriters.EnchantmentRewriter;\nimport com.viaversion.viabackwards.item.DataItemWithExtras;\nimport com.viaversion.viabackwards.protocol.v1_14to1_13_2.Protocol1_14To1_13_2;\nimport com.viaversion.viabackwards.protocol.v1_14to1_13_2.storage.ChunkLightStorage;\nimport com.viaversion.viaversion.api.Via;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.ClientWorld;\nimport com.viaversion.viaversion.api.minecraft.Environment;\nimport com.viaversion.viaversion.api.minecraft.chunks.Chunk;\nimport com.viaversion.viaversion.api.minecraft.chunks.ChunkSection;\nimport com.viaversion.viaversion.api.minecraft.chunks.ChunkSectionLight;\nimport com.viaversion.viaversion.api.minecraft.chunks.ChunkSectionLightImpl;\nimport com.viaversion.viaversion.api.minecraft.chunks.DataPalette;\nimport com.viaversion.viaversion.api.minecraft.chunks.PaletteType;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityType;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_14;\nimport com.viaversion.viaversion.api.minecraft.entitydata.EntityData;\nimport com.viaversion.viaversion.api.minecraft.item.DataItem;\nimport com.viaversion.viaversion.api.minecraft.item.Item;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.chunk.ChunkType1_13;\nimport com.viaversion.viaversion.api.type.types.chunk.ChunkType1_14;\nimport com.viaversion.viaversion.api.type.types.version.Types1_13;\nimport com.viaversion.viaversion.api.type.types.version.Types1_13_2;\nimport com.viaversion.viaversion.libs.gson.JsonElement;\nimport com.viaversion.viaversion.libs.gson.JsonObject;\nimport com.viaversion.viaversion.libs.gson.JsonParseException;\nimport com.viaversion.viaversion.libs.mcstructs.text.utils.TextUtils;\nimport com.viaversion.viaversion.protocols.v1_12_2to1_13.Protocol1_12_2To1_13;\nimport com.viaversion.viaversion.protocols.v1_12_2to1_13.packet.ClientboundPackets1_13;\nimport com.viaversion.viaversion.protocols.v1_12_2to1_13.packet.ServerboundPackets1_13;\nimport com.viaversion.viaversion.protocols.v1_13_2to1_14.Protocol1_13_2To1_14;\nimport com.viaversion.viaversion.protocols.v1_13_2to1_14.packet.ClientboundPackets1_14;\nimport com.viaversion.viaversion.rewriter.RecipeRewriter;\nimport com.viaversion.viaversion.util.ComponentUtil;\nimport com.viaversion.viaversion.util.Key;\nimport com.viaversion.viaversion.util.SerializerVersion;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Set;\n\npublic class BlockItemPacketRewriter1_14 extends BackwardsItemRewriter<ClientboundPackets1_14, ServerboundPackets1_13, Protocol1_14To1_13_2> {\n\n    private EnchantmentRewriter enchantmentRewriter;\n\n    public BlockItemPacketRewriter1_14(Protocol1_14To1_13_2 protocol) {\n        super(protocol, Types.ITEM1_13_2, Types.ITEM1_13_2_SHORT_ARRAY);\n    }\n\n    @Override\n    protected void registerPackets() {\n        protocol.registerServerbound(ServerboundPackets1_13.EDIT_BOOK, wrapper -> handleItemToServer(wrapper.user(), wrapper.passthrough(Types.ITEM1_13_2)));\n\n        protocol.replaceClientbound(ClientboundPackets1_14.OPEN_SCREEN, wrapper -> {\n            int windowId = wrapper.read(Types.VAR_INT);\n            wrapper.write(Types.UNSIGNED_BYTE, (short) windowId);\n\n            int type = wrapper.read(Types.VAR_INT);\n            String stringType = null;\n            String containerTitle = null;\n            int slotSize = 0;\n            if (type < 6) {\n                if (type == 2) containerTitle = \"Barrel\";\n                stringType = \"minecraft:container\";\n                slotSize = (type + 1) * 9;\n            } else {\n                switch (type) {\n                    case 11 -> stringType = \"minecraft:crafting_table\";\n                    case 9, 20, 13, 14 -> {\n                        if (type == 9) containerTitle = \"Blast Furnace\";\n                        else if (type == 20) containerTitle = \"Smoker\";\n                        else if (type == 14) containerTitle = \"Grindstone\";\n                        stringType = \"minecraft:furnace\";\n                        slotSize = 3;\n                    }\n                    case 6 -> {\n                        stringType = \"minecraft:dropper\";\n                        slotSize = 9;\n                    }\n                    case 12 -> stringType = \"minecraft:enchanting_table\";\n                    case 10 -> {\n                        stringType = \"minecraft:brewing_stand\";\n                        slotSize = 5;\n                    }\n                    case 18 -> stringType = \"minecraft:villager\";\n                    case 8 -> {\n                        stringType = \"minecraft:beacon\";\n                        slotSize = 1;\n                    }\n                    case 21, 7 -> {\n                        if (type == 21) containerTitle = \"Cartography Table\";\n                        stringType = \"minecraft:anvil\";\n                    }\n                    case 15 -> {\n                        stringType = \"minecraft:hopper\";\n                        slotSize = 5;\n                    }\n                    case 19 -> {\n                        stringType = \"minecraft:shulker_box\";\n                        slotSize = 27;\n                    }\n                }\n            }\n\n            if (stringType == null) {\n                protocol.getLogger().warning(\"Can't open inventory for player! Type: \" + type);\n                wrapper.cancel();\n                return;\n            }\n\n            wrapper.write(Types.STRING, stringType);\n\n            JsonElement title = wrapper.read(Types.COMPONENT);\n            if (containerTitle != null) {\n                // Don't rewrite renamed, only translatable titles\n                JsonObject object;\n                if (title.isJsonObject() && (object = title.getAsJsonObject()).has(\"translate\")) {\n                    // Don't rewrite other 9x3 translatable containers\n                    if (type != 2 || object.getAsJsonPrimitive(\"translate\").getAsString().equals(\"container.barrel\")) {\n                        title = ComponentUtil.legacyToJson(containerTitle);\n                    }\n                }\n            }\n\n            wrapper.write(Types.COMPONENT, title);\n            wrapper.write(Types.UNSIGNED_BYTE, (short) slotSize);\n        });\n\n        // Horse window -> Open Window\n        protocol.registerClientbound(ClientboundPackets1_14.HORSE_SCREEN_OPEN, ClientboundPackets1_13.OPEN_SCREEN, wrapper -> {\n            wrapper.passthrough(Types.UNSIGNED_BYTE); // Window id\n            wrapper.write(Types.STRING, \"EntityHorse\"); // Type\n            JsonObject object = new JsonObject();\n            object.addProperty(\"translate\", \"minecraft.horse\");\n            wrapper.write(Types.COMPONENT, object); // Title\n            wrapper.write(Types.UNSIGNED_BYTE, wrapper.read(Types.VAR_INT).shortValue()); // Number of slots\n            wrapper.passthrough(Types.INT); // Entity id\n        });\n\n        // Trade List -> Plugin Message\n        protocol.registerClientbound(ClientboundPackets1_14.MERCHANT_OFFERS, ClientboundPackets1_13.CUSTOM_PAYLOAD, wrapper -> {\n            wrapper.write(Types.STRING, \"minecraft:trader_list\");\n\n            int windowId = wrapper.read(Types.VAR_INT);\n            wrapper.write(Types.INT, windowId);\n\n            int size = wrapper.passthrough(Types.UNSIGNED_BYTE);\n            for (int i = 0; i < size; i++) {\n                // Input Item\n                Item input = wrapper.read(Types.ITEM1_13_2);\n                input = handleItemToClient(wrapper.user(), input);\n                wrapper.write(Types.ITEM1_13_2, input);\n\n\n                // Output Item\n                Item output = wrapper.read(Types.ITEM1_13_2);\n                output = handleItemToClient(wrapper.user(), output);\n                wrapper.write(Types.ITEM1_13_2, output);\n\n                boolean secondItem = wrapper.passthrough(Types.BOOLEAN); // Has second item\n                if (secondItem) {\n                    // Second Item\n                    Item second = wrapper.read(Types.ITEM1_13_2);\n                    second = handleItemToClient(wrapper.user(), second);\n                    wrapper.write(Types.ITEM1_13_2, second);\n                }\n\n                wrapper.passthrough(Types.BOOLEAN); // Trade disabled\n                wrapper.passthrough(Types.INT); // Number of tools uses\n                wrapper.passthrough(Types.INT); // Maximum number of trade uses\n\n                wrapper.read(Types.INT);\n                wrapper.read(Types.INT);\n                wrapper.read(Types.FLOAT);\n            }\n            wrapper.read(Types.VAR_INT);\n            wrapper.read(Types.VAR_INT);\n            wrapper.read(Types.BOOLEAN);\n        }, true);\n\n        // Open Book -> Plugin Message\n        protocol.registerClientbound(ClientboundPackets1_14.OPEN_BOOK, ClientboundPackets1_13.CUSTOM_PAYLOAD, wrapper -> {\n            wrapper.write(Types.STRING, \"minecraft:book_open\");\n            wrapper.passthrough(Types.VAR_INT);\n        });\n\n        protocol.replaceClientbound(ClientboundPackets1_14.SET_EQUIPPED_ITEM, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT); // 0 - Entity ID\n                map(Types.VAR_INT); // 1 - Slot ID\n\n                handler(wrapper -> passthroughClientboundItem(wrapper));\n\n                handler(wrapper -> {\n                    int entityId = wrapper.get(Types.VAR_INT, 0);\n                    EntityType entityType = wrapper.user().getEntityTracker(Protocol1_14To1_13_2.class).entityType(entityId);\n                    if (entityType == null) return;\n\n                    if (entityType.isOrHasParent(EntityTypes1_14.ABSTRACT_HORSE)) {\n                        wrapper.setPacketType(ClientboundPackets1_13.SET_ENTITY_DATA);\n                        wrapper.resetReader();\n                        wrapper.passthrough(Types.VAR_INT);\n                        wrapper.read(Types.VAR_INT);\n                        Item item = wrapper.read(Types.ITEM1_13_2);\n                        int armorType = item == null || item.identifier() == 0 ? 0 : item.identifier() - 726;\n                        if (armorType < 0 || armorType > 3) {\n                            wrapper.cancel();\n                            return;\n                        }\n                        List<EntityData> entityDataList = new ArrayList<>();\n                        entityDataList.add(new EntityData(16, Types1_13_2.ENTITY_DATA_TYPES.varIntType, armorType));\n                        wrapper.write(Types1_13.ENTITY_DATA_LIST, entityDataList);\n                    }\n                });\n            }\n        });\n\n        RecipeRewriter<ClientboundPackets1_14> recipeHandler = new RecipeRewriter<>(protocol);\n        final Set<String> removedTypes = ImmutableSet.of(\"crafting_special_suspiciousstew\", \"blasting\", \"smoking\", \"campfire_cooking\", \"stonecutting\");\n        protocol.registerClientbound(ClientboundPackets1_14.UPDATE_RECIPES, wrapper -> {\n            int size = wrapper.passthrough(Types.VAR_INT);\n            int deleted = 0;\n            for (int i = 0; i < size; i++) {\n                String type = wrapper.read(Types.STRING);\n                String id = wrapper.read(Types.STRING); // Recipe Identifier\n                type = Key.stripMinecraftNamespace(type);\n                if (removedTypes.contains(type)) {\n                    switch (type) {\n                        case \"blasting\", \"smoking\", \"campfire_cooking\" -> {\n                            wrapper.read(Types.STRING); // Group\n                            wrapper.read(Types.ITEM1_13_2_ARRAY); // Ingredients\n                            wrapper.read(Types.ITEM1_13_2);\n                            wrapper.read(Types.FLOAT); // EXP\n                            wrapper.read(Types.VAR_INT); // Cooking time\n                        }\n                        case \"stonecutting\" -> {\n                            wrapper.read(Types.STRING); // Group?\n                            wrapper.read(Types.ITEM1_13_2_ARRAY); // Ingredients\n                            wrapper.read(Types.ITEM1_13_2); // Result\n                        }\n                    }\n                    deleted++;\n                    continue;\n                }\n                wrapper.write(Types.STRING, id);\n                wrapper.write(Types.STRING, type);\n\n                // Handle the rest of the types\n                recipeHandler.handleRecipeType(wrapper, type);\n            }\n            wrapper.set(Types.VAR_INT, 0, size - deleted);\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_14.BLOCK_DESTRUCTION, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT);\n                map(Types.BLOCK_POSITION1_14, Types.BLOCK_POSITION1_8);\n                map(Types.BYTE);\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_14.BLOCK_ENTITY_DATA, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.BLOCK_POSITION1_14, Types.BLOCK_POSITION1_8);\n            }\n        });\n\n        protocol.replaceClientbound(ClientboundPackets1_14.BLOCK_EVENT, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.BLOCK_POSITION1_14, Types.BLOCK_POSITION1_8); // Location\n                map(Types.UNSIGNED_BYTE); // Action id\n                map(Types.UNSIGNED_BYTE); // Action param\n                map(Types.VAR_INT); // Block id - /!\\ NOT BLOCK STATE\n                handler(wrapper -> {\n                    int mappedId = protocol.getMappingData().getNewBlockId(wrapper.get(Types.VAR_INT, 0));\n                    if (mappedId == -1) {\n                        wrapper.cancel();\n                        return;\n                    }\n                    wrapper.set(Types.VAR_INT, 0, mappedId);\n                });\n            }\n        });\n\n        protocol.replaceClientbound(ClientboundPackets1_14.BLOCK_UPDATE, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.BLOCK_POSITION1_14, Types.BLOCK_POSITION1_8);\n                map(Types.VAR_INT);\n                handler(wrapper -> {\n                    int id = wrapper.get(Types.VAR_INT, 0);\n\n                    wrapper.set(Types.VAR_INT, 0, protocol.getMappingData().getNewBlockStateId(id));\n                });\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_14.EXPLODE, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.FLOAT); // X\n                map(Types.FLOAT); // Y\n                map(Types.FLOAT); // Z\n                map(Types.FLOAT); // Radius\n                handler(wrapper -> {\n                    for (int i = 0; i < 3; i++) {\n                        float coord = wrapper.get(Types.FLOAT, i);\n\n                        if (coord < 0f) {\n                            coord = (float) Math.floor(coord);\n                            wrapper.set(Types.FLOAT, i, coord);\n                        }\n                    }\n                });\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_14.LEVEL_CHUNK, wrapper -> {\n            ClientWorld clientWorld = wrapper.user().getClientWorld(Protocol1_14To1_13_2.class);\n            Chunk chunk = wrapper.read(ChunkType1_14.TYPE);\n            wrapper.write(ChunkType1_13.forEnvironment(clientWorld.getEnvironment()), chunk);\n\n            ChunkLightStorage.ChunkLight chunkLight = wrapper.user().get(ChunkLightStorage.class).getStoredLight(chunk.getX(), chunk.getZ());\n            for (int i = 0; i < chunk.getSections().length; i++) {\n                ChunkSection section = chunk.getSections()[i];\n                if (section == null) continue;\n\n                ChunkSectionLight sectionLight = ChunkSectionLightImpl.createWithBlockLight();\n                section.setLight(sectionLight);\n                if (chunkLight == null) {\n                    sectionLight.setBlockLight(ChunkLightStorage.FULL_LIGHT);\n                    if (clientWorld.getEnvironment() == Environment.NORMAL) {\n                        sectionLight.setSkyLight(ChunkLightStorage.FULL_LIGHT);\n                    }\n                } else {\n                    byte[] blockLight = chunkLight.blockLight()[i];\n                    sectionLight.setBlockLight(blockLight != null ? blockLight : ChunkLightStorage.FULL_LIGHT);\n                    if (clientWorld.getEnvironment() == Environment.NORMAL) {\n                        byte[] skyLight = chunkLight.skyLight()[i];\n                        sectionLight.setSkyLight(skyLight != null ? skyLight : ChunkLightStorage.FULL_LIGHT);\n                    }\n                }\n\n                DataPalette palette = section.palette(PaletteType.BLOCKS);\n                if (Via.getConfig().isNonFullBlockLightFix() && section.getNonAirBlocksCount() != 0 && sectionLight.hasBlockLight()) {\n                    for (int x = 0; x < 16; x++) {\n                        for (int y = 0; y < 16; y++) {\n                            for (int z = 0; z < 16; z++) {\n                                int id = palette.idAt(x, y, z);\n                                if (Protocol1_13_2To1_14.MAPPINGS.getNonFullBlocks().contains(id)) {\n                                    sectionLight.getBlockLightNibbleArray().set(x, y, z, 0);\n                                }\n                            }\n                        }\n                    }\n                }\n\n                for (int j = 0; j < palette.size(); j++) {\n                    int mappedBlockStateId = protocol.getMappingData().getNewBlockStateId(palette.idByIndex(j));\n                    palette.setIdByIndex(j, mappedBlockStateId);\n                }\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_14.FORGET_LEVEL_CHUNK, wrapper -> {\n            int x = wrapper.passthrough(Types.INT);\n            int z = wrapper.passthrough(Types.INT);\n            wrapper.user().get(ChunkLightStorage.class).unloadChunk(x, z);\n        });\n\n        protocol.replaceClientbound(ClientboundPackets1_14.LEVEL_EVENT, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.INT); // Effect Id\n                map(Types.BLOCK_POSITION1_14, Types.BLOCK_POSITION1_8); // Location\n                map(Types.INT); // Data\n                handler(wrapper -> {\n                    int id = wrapper.get(Types.INT, 0);\n                    int data = wrapper.get(Types.INT, 1);\n                    if (id == 1010) { // Play record\n                        wrapper.set(Types.INT, 1, protocol.getMappingData().getNewItemId(data));\n                    } else if (id == 2001) { // Block break + block break sound\n                        wrapper.set(Types.INT, 1, protocol.getMappingData().getNewBlockStateId(data));\n                    }\n                });\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_14.MAP_ITEM_DATA, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT);\n                map(Types.BYTE);\n                map(Types.BOOLEAN);\n                read(Types.BOOLEAN); // Locked\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_14.SET_DEFAULT_SPAWN_POSITION, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.BLOCK_POSITION1_14, Types.BLOCK_POSITION1_8);\n            }\n        });\n    }\n\n    @Override\n    protected void registerRewrites() {\n        enchantmentRewriter = new EnchantmentRewriter(this, false);\n        enchantmentRewriter.registerEnchantment(\"minecraft:multishot\", \"§7Multishot\");\n        enchantmentRewriter.registerEnchantment(\"minecraft:quick_charge\", \"§7Quick Charge\");\n        enchantmentRewriter.registerEnchantment(\"minecraft:piercing\", \"§7Piercing\");\n    }\n\n    @Override\n    public Item handleItemToClient(UserConnection connection, Item item) {\n        if (item == null) return null;\n        item = super.handleItemToClient(connection, item);\n\n        // Lore now uses JSON\n        final CompoundTag display = item.tag() != null ? item.tag().getCompoundTag(\"display\") : null;\n        if (display != null && item instanceof DataItemWithExtras fullItem && fullItem.lore() != null) {\n            final List<JsonElement> lore = fullItem.lore();\n            final ListTag<StringTag> loreTag = fullItem.rawLore();\n            saveListTag(display, loreTag, \"Lore\");\n\n            try {\n                for (int i = 0; i < lore.size(); i++) {\n                    final JsonElement loreEntry = lore.get(i);\n                    final var component = SerializerVersion.V1_12.toComponent(loreEntry);\n                    if (component == null) {\n                        lore.remove(i);\n                        loreTag.remove(i);\n                        i--;\n                        continue;\n                    }\n\n                    TextUtils.setTranslator(component, s -> Protocol1_12_2To1_13.MAPPINGS.getMojangTranslation()\n                        .getOrDefault(s, TranslatableMappings.getTranslatableMappings(\"1.14\").get(s)));\n                    loreTag.get(i).setValue(component.asLegacyFormatString());\n                }\n            } catch (final JsonParseException e) {\n                display.remove(\"Lore\");\n            }\n        }\n        if (item instanceof DataItemWithExtras) {\n            item = new DataItem(item.identifier(), (byte) item.amount(), item.data(), item.tag()); // back to a normal DataItem\n        }\n\n        enchantmentRewriter.handleToClient(item);\n        return item;\n    }\n\n    @Override\n    public Item handleItemToServer(UserConnection connection, Item item) {\n        if (item == null) return null;\n\n        // Lore now uses JSON\n        CompoundTag tag = item.tag();\n        CompoundTag display;\n        if (tag != null && (display = tag.getCompoundTag(\"display\")) != null) {\n            // Transform to json if no backup tag is found (else process that in the super method)\n            ListTag<StringTag> lore = display.getListTag(\"Lore\", StringTag.class);\n            if (lore != null && !hasBackupTag(display, \"Lore\")) {\n                for (StringTag loreEntry : lore) {\n                    loreEntry.setValue(ComponentUtil.legacyToJsonString(loreEntry.getValue()));\n                }\n            }\n        }\n\n        enchantmentRewriter.handleToServer(item);\n\n        // Call this last to check for the backup lore above\n        item = super.handleItemToServer(connection, item);\n        return item;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_14to1_13_2/rewriter/CommandRewriter1_14.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_14to1_13_2.rewriter;\n\nimport com.viaversion.viabackwards.protocol.v1_14to1_13_2.Protocol1_14To1_13_2;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.protocols.v1_13_2to1_14.packet.ClientboundPackets1_14;\nimport com.viaversion.viaversion.rewriter.CommandRewriter;\nimport org.checkerframework.checker.nullness.qual.Nullable;\n\npublic class CommandRewriter1_14 extends CommandRewriter<ClientboundPackets1_14> {\n\n    public CommandRewriter1_14(Protocol1_14To1_13_2 protocol) {\n        super(protocol);\n\n        this.parserHandlers.put(\"minecraft:nbt_tag\", wrapper -> wrapper.write(Types.VAR_INT, 2)); // Greedy phrase\n        this.parserHandlers.put(\"minecraft:time\", wrapper -> {\n            wrapper.write(Types.BYTE, (byte) (0x01)); // Flags\n            wrapper.write(Types.INT, 0); // Min value\n        });\n    }\n\n    @Override\n    public @Nullable String handleArgumentType(String argumentType) {\n        return switch (argumentType) {\n            case \"minecraft:nbt_compound_tag\" -> \"minecraft:nbt\";\n            case \"minecraft:nbt_tag\" -> \"brigadier:string\";\n            case \"minecraft:time\" -> \"brigadier:integer\";\n            default -> super.handleArgumentType(argumentType);\n        };\n    }\n\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_14to1_13_2/rewriter/EntityPacketRewriter1_14.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_14to1_13_2.rewriter;\n\nimport com.viaversion.viabackwards.api.entities.storage.EntityPositionHandler;\nimport com.viaversion.viabackwards.api.entities.storage.EntityReplacement;\nimport com.viaversion.viabackwards.api.rewriters.LegacyEntityRewriter;\nimport com.viaversion.viabackwards.protocol.v1_14to1_13_2.Protocol1_14To1_13_2;\nimport com.viaversion.viabackwards.protocol.v1_14to1_13_2.storage.ChunkLightStorage;\nimport com.viaversion.viabackwards.protocol.v1_14to1_13_2.storage.DifficultyStorage;\nimport com.viaversion.viabackwards.protocol.v1_14to1_13_2.storage.EntityPositionStorage1_14;\nimport com.viaversion.viaversion.api.data.entity.EntityTracker;\nimport com.viaversion.viaversion.api.data.entity.StoredEntityData;\nimport com.viaversion.viaversion.api.minecraft.BlockPosition;\nimport com.viaversion.viaversion.api.minecraft.ClientWorld;\nimport com.viaversion.viaversion.api.minecraft.Particle;\nimport com.viaversion.viaversion.api.minecraft.VillagerData;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityType;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_13;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_14;\nimport com.viaversion.viaversion.api.minecraft.entitydata.EntityData;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandler;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.version.Types1_13_2;\nimport com.viaversion.viaversion.api.type.types.version.Types1_14;\nimport com.viaversion.viaversion.protocols.v1_12_2to1_13.packet.ClientboundPackets1_13;\nimport com.viaversion.viaversion.protocols.v1_12_2to1_13.packet.ServerboundPackets1_13;\nimport com.viaversion.viaversion.protocols.v1_13_2to1_14.packet.ClientboundPackets1_14;\nimport com.viaversion.viaversion.rewriter.entitydata.EntityDataHandler;\n\npublic class EntityPacketRewriter1_14 extends LegacyEntityRewriter<ClientboundPackets1_14, Protocol1_14To1_13_2> {\n\n    private EntityPositionHandler positionHandler;\n\n    public EntityPacketRewriter1_14(Protocol1_14To1_13_2 protocol) {\n        super(protocol, Types1_13_2.ENTITY_DATA_TYPES.optionalComponentType, Types1_13_2.ENTITY_DATA_TYPES.booleanType);\n    }\n\n    @Override\n    protected void registerPackets() {\n        positionHandler = new EntityPositionHandler(this, EntityPositionStorage1_14.class, EntityPositionStorage1_14::new);\n\n        protocol.registerClientbound(ClientboundPackets1_14.ENTITY_EVENT, wrapper -> {\n            int entityId = wrapper.passthrough(Types.INT);\n            byte status = wrapper.passthrough(Types.BYTE);\n            // Check for death status\n            if (status != 3) return;\n\n            EntityTracker tracker = tracker(wrapper.user());\n            EntityType entityType = tracker.entityType(entityId);\n            if (entityType != EntityTypes1_14.PLAYER) return;\n\n            // Remove equipment, else the client will see ghost items\n            for (int i = 0; i <= 5; i++) {\n                PacketWrapper equipmentPacket = wrapper.create(ClientboundPackets1_13.SET_EQUIPPED_ITEM);\n                equipmentPacket.write(Types.VAR_INT, entityId);\n                equipmentPacket.write(Types.VAR_INT, i);\n                equipmentPacket.write(Types.ITEM1_13_2, null);\n                equipmentPacket.send(Protocol1_14To1_13_2.class);\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_14.TELEPORT_ENTITY, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT);\n                map(Types.DOUBLE);\n                map(Types.DOUBLE);\n                map(Types.DOUBLE);\n                handler(wrapper -> positionHandler.cacheEntityPosition(wrapper, false, false));\n            }\n        });\n\n        PacketHandlers relativeMoveHandler = new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT);\n                map(Types.SHORT);\n                map(Types.SHORT);\n                map(Types.SHORT);\n                handler(wrapper -> {\n                    double x = wrapper.get(Types.SHORT, 0) / EntityPositionHandler.RELATIVE_MOVE_FACTOR;\n                    double y = wrapper.get(Types.SHORT, 1) / EntityPositionHandler.RELATIVE_MOVE_FACTOR;\n                    double z = wrapper.get(Types.SHORT, 2) / EntityPositionHandler.RELATIVE_MOVE_FACTOR;\n                    positionHandler.cacheEntityPosition(wrapper, x, y, z, false, true);\n                });\n            }\n        };\n        protocol.registerClientbound(ClientboundPackets1_14.MOVE_ENTITY_POS, relativeMoveHandler);\n        protocol.registerClientbound(ClientboundPackets1_14.MOVE_ENTITY_POS_ROT, relativeMoveHandler);\n\n        protocol.registerClientbound(ClientboundPackets1_14.ADD_ENTITY, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT); // 0 - Entity id\n                map(Types.UUID); // 1 - UUID\n                map(Types.VAR_INT, Types.BYTE); // 2 - Type\n                map(Types.DOUBLE); // 3 - X\n                map(Types.DOUBLE); // 4 - Y\n                map(Types.DOUBLE); // 5 - Z\n                map(Types.BYTE); // 6 - Pitch\n                map(Types.BYTE); // 7 - Yaw\n                map(Types.INT); // 8 - Data\n                map(Types.SHORT); // 9 - Velocity X\n                map(Types.SHORT); // 10 - Velocity Y\n                map(Types.SHORT); // 11 - Velocity Z\n\n                handler(wrapper -> {\n                    final int type = wrapper.get(Types.BYTE, 0);\n                    final int data = wrapper.get(Types.INT, 0);\n\n                    final EntityType entityType = objectTypeFromId(type, data);\n                    if (entityType == null) {\n                        return;\n                    }\n\n                    trackAndCacheEntityPosition(wrapper, entityType);\n                });\n\n                handler(wrapper -> {\n                    int id = wrapper.get(Types.BYTE, 0);\n                    int mappedId = newEntityId(id);\n                    EntityTypes1_13.EntityType entityType = EntityTypes1_13.EntityType.findById(mappedId);\n                    if (entityType == null) {\n                        // Would be EntityType.PIG on a 1.14 client, but later discarded anyway since not an object type\n                        return;\n                    }\n\n                    EntityTypes1_13.ObjectType objectType = null;\n                    if (entityType.isOrHasParent(EntityTypes1_13.EntityType.ABSTRACT_MINECART)) {\n                        objectType = EntityTypes1_13.ObjectType.MINECART;\n                        int data = switch (entityType) {\n                            case CHEST_MINECART -> 1;\n                            case FURNACE_MINECART -> 2;\n                            case TNT_MINECART -> 3;\n                            case SPAWNER_MINECART -> 4;\n                            case HOPPER_MINECART -> 5;\n                            case COMMAND_BLOCK_MINECART -> 6;\n                            default -> 0;\n                        };\n                        if (data != 0)\n                            wrapper.set(Types.INT, 0, data);\n                    } else if (entityType.is(EntityTypes1_13.EntityType.EXPERIENCE_ORB)) {\n                        // Newer clients can spawn experience orbs via add entity, map to add experience orb and override values via multiple packets\n                        wrapper.cancel();\n                        final int entityId = wrapper.get(Types.VAR_INT, 0);\n\n                        // Shrug about uuid or rotations\n                        final PacketWrapper addExperienceOrb = PacketWrapper.create(ClientboundPackets1_13.ADD_EXPERIENCE_ORB, wrapper.user());\n                        addExperienceOrb.write(Types.VAR_INT, entityId); // Entity id\n                        addExperienceOrb.write(Types.DOUBLE, wrapper.get(Types.DOUBLE, 0)); // X\n                        addExperienceOrb.write(Types.DOUBLE, wrapper.get(Types.DOUBLE, 1)); // Y\n                        addExperienceOrb.write(Types.DOUBLE, wrapper.get(Types.DOUBLE, 2)); // Z\n                        addExperienceOrb.write(Types.SHORT, (short) 0); // Experience count\n                        addExperienceOrb.send(Protocol1_14To1_13_2.class);\n\n                        final PacketWrapper setEntityMotion = PacketWrapper.create(ClientboundPackets1_13.SET_ENTITY_MOTION, wrapper.user());\n                        setEntityMotion.write(Types.VAR_INT, entityId); // Entity id\n                        setEntityMotion.write(Types.SHORT, wrapper.get(Types.SHORT, 0));\n                        setEntityMotion.write(Types.SHORT, wrapper.get(Types.SHORT, 1));\n                        setEntityMotion.write(Types.SHORT, wrapper.get(Types.SHORT, 2));\n                        setEntityMotion.send(Protocol1_14To1_13_2.class);\n                        return;\n                    } else {\n                        for (final EntityTypes1_13.ObjectType type : EntityTypes1_13.ObjectType.values()) {\n                            if (type.getType() == entityType) {\n                                objectType = type;\n                                break;\n                            }\n                        }\n                    }\n\n                    if (objectType == null) return;\n\n                    wrapper.set(Types.BYTE, 0, (byte) objectType.getId());\n\n                    int data = wrapper.get(Types.INT, 0);\n                    if (objectType == EntityTypes1_13.ObjectType.FALLING_BLOCK) {\n                        int blockState = wrapper.get(Types.INT, 0);\n                        int combined = protocol.getMappingData().getNewBlockStateId(blockState);\n                        wrapper.set(Types.INT, 0, combined);\n                    } else if (entityType.isOrHasParent(EntityTypes1_13.EntityType.ABSTRACT_ARROW)) {\n                        wrapper.set(Types.INT, 0, data + 1);\n                    }\n                });\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_14.ADD_MOB, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT); // 0 - Entity ID\n                map(Types.UUID); // 1 - Entity UUID\n                map(Types.VAR_INT); // 2 - Entity Type\n                map(Types.DOUBLE); // 3 - X\n                map(Types.DOUBLE); // 4 - Y\n                map(Types.DOUBLE); // 5 - Z\n                map(Types.BYTE); // 6 - Yaw\n                map(Types.BYTE); // 7 - Pitch\n                map(Types.BYTE); // 8 - Head Pitch\n                map(Types.SHORT); // 9 - Velocity X\n                map(Types.SHORT); // 10 - Velocity Y\n                map(Types.SHORT); // 11 - Velocity Z\n                map(Types1_14.ENTITY_DATA_LIST, Types1_13_2.ENTITY_DATA_LIST); // 12 - Entity data\n\n                handler(wrapper -> {\n                    int type = wrapper.get(Types.VAR_INT, 1);\n                    EntityType entityType = EntityTypes1_14.getTypeFromId(type);\n                    trackAndCacheEntityPosition(wrapper, entityType);\n\n                    int oldId = newEntityId(type);\n                    if (oldId == -1) {\n                        EntityReplacement entityReplacement = entityDataForType(entityType);\n                        if (entityReplacement == null) {\n                            protocol.getLogger().warning(\"Could not find entity type mapping \" + type + \"/\" + entityType);\n                            wrapper.cancel();\n                        } else {\n                            wrapper.set(Types.VAR_INT, 1, entityReplacement.replacementId());\n                        }\n                    } else {\n                        wrapper.set(Types.VAR_INT, 1, oldId);\n                    }\n                });\n\n                // Handle entity type & data\n                handler(getMobSpawnRewriter1_11(Types1_13_2.ENTITY_DATA_LIST));\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_14.ADD_EXPERIENCE_ORB, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT); // 0 - Entity id\n                map(Types.DOUBLE); // Needs to be mapped for the position cache\n                map(Types.DOUBLE);\n                map(Types.DOUBLE);\n                handler(wrapper -> trackAndCacheEntityPosition(wrapper, EntityTypes1_14.EXPERIENCE_ORB));\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_14.ADD_GLOBAL_ENTITY, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT); // 0 - Entity id\n                map(Types.BYTE);\n                map(Types.DOUBLE); // Needs to be mapped for the position cache\n                map(Types.DOUBLE);\n                map(Types.DOUBLE);\n                handler(wrapper -> trackAndCacheEntityPosition(wrapper, EntityTypes1_14.LIGHTNING_BOLT));\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_14.ADD_PAINTING, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT);\n                map(Types.UUID);\n                map(Types.VAR_INT);\n                map(Types.BLOCK_POSITION1_14, Types.BLOCK_POSITION1_8);\n                map(Types.BYTE);\n\n                // Track entity\n                handler(wrapper -> trackAndCacheEntityPosition(wrapper, EntityTypes1_14.PAINTING));\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_14.ADD_PLAYER, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT); // 0 - Entity ID\n                map(Types.UUID); // 1 - Player UUID\n                map(Types.DOUBLE); // 2 - X\n                map(Types.DOUBLE); // 3 - Y\n                map(Types.DOUBLE); // 4 - Z\n                map(Types.BYTE); // 5 - Yaw\n                map(Types.BYTE); // 6 - Pitch\n                map(Types1_14.ENTITY_DATA_LIST, Types1_13_2.ENTITY_DATA_LIST); // 7 - Entity data\n\n                handler(getTrackerAndDataHandler(Types1_13_2.ENTITY_DATA_LIST, EntityTypes1_14.PLAYER));\n                handler(wrapper -> positionHandler.cacheEntityPosition(wrapper, true, false));\n            }\n        });\n\n        registerSetEntityData(ClientboundPackets1_14.SET_ENTITY_DATA, Types1_14.ENTITY_DATA_LIST, Types1_13_2.ENTITY_DATA_LIST);\n\n        protocol.registerClientbound(ClientboundPackets1_14.LOGIN, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.INT); // 0 - Entity ID\n                map(Types.UNSIGNED_BYTE); // 1 - Gamemode\n                map(Types.INT); // 2 - Dimension\n\n                handler(getDimensionHandler(1));\n                handler(getPlayerTrackerHandler());\n                handler(wrapper -> {\n                    short difficulty = wrapper.user().get(DifficultyStorage.class).getDifficulty();\n                    wrapper.write(Types.UNSIGNED_BYTE, difficulty);\n\n                    wrapper.passthrough(Types.UNSIGNED_BYTE); // Max Players\n                    wrapper.passthrough(Types.STRING); // Level Type\n                    wrapper.read(Types.VAR_INT); // Read View Distance\n\n                    final int entityId = wrapper.get(Types.INT, 0);\n\n                    final StoredEntityData storedEntity = tracker(wrapper.user()).entityData(entityId);\n                    storedEntity.put(new EntityPositionStorage1_14());\n                });\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_14.RESPAWN, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.INT); // 0 - Dimension ID\n\n                handler(wrapper -> {\n                    ClientWorld clientWorld = wrapper.user().getClientWorld(Protocol1_14To1_13_2.class);\n                    int dimensionId = wrapper.get(Types.INT, 0);\n\n                    if (clientWorld.setEnvironment(dimensionId)) {\n                        EntityTracker tracker = tracker(wrapper.user());\n                        tracker.clearEntities();\n                        wrapper.user().get(ChunkLightStorage.class).clear();\n                        tracker.entityData(tracker.clientEntityId()).put(new EntityPositionStorage1_14());\n                    }\n\n                    short difficulty = wrapper.user().get(DifficultyStorage.class).getDifficulty();\n                    wrapper.write(Types.UNSIGNED_BYTE, difficulty);\n                });\n            }\n        });\n\n        PacketHandler absoluteMoveHandler = wrapper -> {\n            final double x = wrapper.passthrough(Types.DOUBLE);\n            final double y = wrapper.passthrough(Types.DOUBLE);\n            final double z = wrapper.passthrough(Types.DOUBLE);\n            positionHandler.cacheEntityPosition(wrapper, tracker(wrapper.user()).clientEntityId(), x, y, z, false, false);\n        };\n        protocol.registerServerbound(ServerboundPackets1_13.MOVE_PLAYER_POS, absoluteMoveHandler);\n        protocol.registerServerbound(ServerboundPackets1_13.MOVE_PLAYER_POS_ROT, absoluteMoveHandler);\n    }\n\n    private void trackAndCacheEntityPosition(PacketWrapper wrapper, EntityType type) {\n        // Tracks the entity + cache the position for the entity\n        tracker(wrapper.user()).addEntity(wrapper.get(Types.VAR_INT, 0), type);\n\n        if (type == EntityTypes1_14.PAINTING) {\n            final BlockPosition position = wrapper.get(Types.BLOCK_POSITION1_8, 0);\n            positionHandler.cacheEntityPosition(wrapper, position.x(), position.y(), position.z(), true, false);\n        } else {\n            positionHandler.cacheEntityPosition(wrapper, true, false);\n        }\n    }\n\n    @Override\n    protected void registerRewrites() {\n        filter().handler((event, data) -> {\n            int typeId = data.dataType().typeId();\n            if (typeId <= 15) {\n                data.setDataType(Types1_13_2.ENTITY_DATA_TYPES.byId(typeId));\n            }\n        });\n\n        registerEntityDataTypeHandler(Types1_13_2.ENTITY_DATA_TYPES.itemType, null, Types1_13_2.ENTITY_DATA_TYPES.optionalBlockStateType, null,\n            Types1_13_2.ENTITY_DATA_TYPES.componentType, Types1_13_2.ENTITY_DATA_TYPES.optionalComponentType);\n\n        filter().type(EntityTypes1_14.PILLAGER).cancel(15);\n\n        filter().type(EntityTypes1_14.FOX).cancel(15);\n        filter().type(EntityTypes1_14.FOX).cancel(16);\n        filter().type(EntityTypes1_14.FOX).cancel(17);\n        filter().type(EntityTypes1_14.FOX).cancel(18);\n\n        filter().type(EntityTypes1_14.PANDA).cancel(15);\n        filter().type(EntityTypes1_14.PANDA).cancel(16);\n        filter().type(EntityTypes1_14.PANDA).cancel(17);\n        filter().type(EntityTypes1_14.PANDA).cancel(18);\n        filter().type(EntityTypes1_14.PANDA).cancel(19);\n        filter().type(EntityTypes1_14.PANDA).cancel(20);\n\n        filter().type(EntityTypes1_14.CAT).cancel(18);\n        filter().type(EntityTypes1_14.CAT).cancel(19);\n        filter().type(EntityTypes1_14.CAT).cancel(20);\n\n        filter().type(EntityTypes1_14.ABSTRACT_RAIDER).removeIndex(14); // Celebrating\n\n        filter().type(EntityTypes1_14.AREA_EFFECT_CLOUD).index(10).handler((event, data) -> {\n            protocol.getParticleRewriter().rewriteParticle(event.user(), (Particle) data.getValue());\n        });\n\n        filter().type(EntityTypes1_14.FIREWORK_ROCKET).index(8).handler((event, data) -> {\n            data.setDataType(Types1_13_2.ENTITY_DATA_TYPES.varIntType);\n            Integer value = (Integer) data.getValue();\n            if (value == null) {\n                data.setValue(0);\n            }\n        });\n\n        filter().type(EntityTypes1_14.ABSTRACT_ARROW).removeIndex(9);\n\n        filter().type(EntityTypes1_14.VILLAGER).cancel(15); // Head shake timer\n\n        EntityDataHandler villagerDataHandler = (event, data) -> {\n            VillagerData villagerData = (VillagerData) data.getValue();\n            data.setTypeAndValue(Types1_13_2.ENTITY_DATA_TYPES.varIntType, villagerDataToProfession(villagerData));\n            if (data.id() == 16) {\n                event.setIndex(15); // decreased by 2 again in one of the following handlers\n            }\n        };\n\n        filter().type(EntityTypes1_14.ZOMBIE_VILLAGER).index(18).handler(villagerDataHandler);\n        filter().type(EntityTypes1_14.VILLAGER).index(16).handler(villagerDataHandler);\n\n        // Holding arms up - from bitfield into own boolean\n        filter().type(EntityTypes1_14.ABSTRACT_SKELETON).index(13).handler((event, data) -> {\n            byte value = (byte) data.getValue();\n            if ((value & 4) != 0) {\n                event.createExtraData(new EntityData(14, Types1_13_2.ENTITY_DATA_TYPES.booleanType, true));\n            }\n        });\n        filter().type(EntityTypes1_14.ZOMBIE).index(13).handler((event, data) -> {\n            byte value = (byte) data.getValue();\n            if ((value & 4) != 0) {\n                event.createExtraData(new EntityData(16, Types1_13_2.ENTITY_DATA_TYPES.booleanType, true));\n            }\n        });\n\n        filter().type(EntityTypes1_14.ZOMBIE).addIndex(16);\n\n        // Remove bed location\n        filter().type(EntityTypes1_14.LIVING_ENTITY).handler((event, data) -> {\n            int index = event.index();\n            if (index == 12) {\n                BlockPosition position = (BlockPosition) data.getValue();\n                if (position != null) {\n                    // Use bed\n                    PacketWrapper wrapper = PacketWrapper.create(ClientboundPackets1_13.PLAYER_SLEEP, null, event.user());\n                    wrapper.write(Types.VAR_INT, event.entityId());\n                    wrapper.write(Types.BLOCK_POSITION1_8, position);\n\n                    try {\n                        wrapper.scheduleSend(Protocol1_14To1_13_2.class);\n                    } catch (Exception ex) {\n                        ex.printStackTrace();\n                    }\n                }\n\n                event.cancel();\n            } else if (index > 12) {\n                event.setIndex(index - 1);\n            }\n        });\n\n        // Pose\n        filter().removeIndex(6);\n\n        filter().type(EntityTypes1_14.OCELOT).index(13).handler((event, data) -> {\n            event.setIndex(15);\n            data.setTypeAndValue(Types1_13_2.ENTITY_DATA_TYPES.varIntType, 0);\n        });\n\n        filter().type(EntityTypes1_14.CAT).handler((event, data) -> {\n            if (event.index() == 15) {\n                data.setValue(1);\n            } else if (event.index() == 13) {\n                data.setValue((byte) ((byte) data.getValue() & 0x4));\n            }\n        });\n\n        filter().handler((event, data) -> {\n            if (data.dataType().typeId() > 15) {\n                event.cancel(); // Cancel bad data (generally from plugins sending data for despawned entities)\n            }\n        });\n    }\n\n    public int villagerDataToProfession(VillagerData data) {\n        switch (data.profession()) {\n            case 1: // Armorer\n            case 10: // Mason\n            case 13: // Toolsmith\n            case 14: // Weaponsmith\n                return 3; // Blacksmith\n            case 2: // Butcher\n            case 8: // Leatherworker\n                return 4; // Butcher\n            case 3: // Cartographer\n            case 9: // Librarian\n                return 1; // Librarian\n            case 4: // Cleric\n                return 2; // Priest\n            case 5: // Farmer\n            case 6: // Fisherman\n            case 7: // Fletcher\n            case 12: // Shepherd\n                return 0; // Farmer\n            case 0: // None\n            case 11: // Nitwit\n            default:\n                return 5; // Nitwit\n        }\n    }\n\n    @Override\n    public void onMappingDataLoaded() {\n        super.onMappingDataLoaded();\n        mapEntityTypeWithData(EntityTypes1_14.CAT, EntityTypes1_14.OCELOT).jsonName();\n        mapEntityTypeWithData(EntityTypes1_14.TRADER_LLAMA, EntityTypes1_14.LLAMA).jsonName();\n        mapEntityTypeWithData(EntityTypes1_14.FOX, EntityTypes1_14.WOLF).jsonName();\n        mapEntityTypeWithData(EntityTypes1_14.PANDA, EntityTypes1_14.POLAR_BEAR).jsonName();\n        mapEntityTypeWithData(EntityTypes1_14.PILLAGER, EntityTypes1_14.VILLAGER).jsonName();\n        mapEntityTypeWithData(EntityTypes1_14.WANDERING_TRADER, EntityTypes1_14.VILLAGER).jsonName();\n        mapEntityTypeWithData(EntityTypes1_14.RAVAGER, EntityTypes1_14.COW).jsonName();\n    }\n\n    @Override\n    public EntityType typeFromId(int typeId) {\n        return EntityTypes1_14.getTypeFromId(typeId);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_14to1_13_2/rewriter/PlayerPacketRewriter1_14.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_14to1_13_2.rewriter;\n\nimport com.viaversion.viabackwards.protocol.v1_14to1_13_2.Protocol1_14To1_13_2;\nimport com.viaversion.viabackwards.protocol.v1_14to1_13_2.storage.DifficultyStorage;\nimport com.viaversion.viaversion.api.minecraft.BlockPosition;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.rewriter.RewriterBase;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.protocols.v1_12_2to1_13.packet.ServerboundPackets1_13;\nimport com.viaversion.viaversion.protocols.v1_13_2to1_14.packet.ClientboundPackets1_14;\n\npublic class PlayerPacketRewriter1_14 extends RewriterBase<Protocol1_14To1_13_2> {\n\n    public PlayerPacketRewriter1_14(Protocol1_14To1_13_2 protocol) {\n        super(protocol);\n    }\n\n    @Override\n    protected void registerPackets() {\n        protocol.registerClientbound(ClientboundPackets1_14.CHANGE_DIFFICULTY, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.UNSIGNED_BYTE);\n                read(Types.BOOLEAN); // Locked\n                handler(wrapper -> {\n                    byte difficulty = wrapper.get(Types.UNSIGNED_BYTE, 0).byteValue();\n                    wrapper.user().get(DifficultyStorage.class).setDifficulty(difficulty);\n                });\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_14.OPEN_SIGN_EDITOR, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.BLOCK_POSITION1_14, Types.BLOCK_POSITION1_8);\n            }\n        });\n        protocol.registerServerbound(ServerboundPackets1_13.BLOCK_ENTITY_TAG_QUERY, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT);\n                map(Types.BLOCK_POSITION1_8, Types.BLOCK_POSITION1_14);\n            }\n        });\n        protocol.registerServerbound(ServerboundPackets1_13.PLAYER_ACTION, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT); // Action\n                map(Types.BLOCK_POSITION1_8, Types.BLOCK_POSITION1_14); // Position\n            }\n        });\n\n        protocol.registerServerbound(ServerboundPackets1_13.RECIPE_BOOK_UPDATE, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT);\n                handler(wrapper -> {\n                    int type = wrapper.get(Types.VAR_INT, 0);\n                    if (type == 0) {\n                        wrapper.passthrough(Types.STRING);\n                    } else if (type == 1) {\n                        wrapper.passthrough(Types.BOOLEAN); // Crafting Recipe Book Open\n                        wrapper.passthrough(Types.BOOLEAN); // Crafting Recipe Filter Active\n                        wrapper.passthrough(Types.BOOLEAN); // Smelting Recipe Book Open\n                        wrapper.passthrough(Types.BOOLEAN); // Smelting Recipe Filter Active\n\n                        // Blast furnace/smoker data\n                        wrapper.write(Types.BOOLEAN, false);\n                        wrapper.write(Types.BOOLEAN, false);\n                        wrapper.write(Types.BOOLEAN, false);\n                        wrapper.write(Types.BOOLEAN, false);\n                    }\n                });\n            }\n        });\n\n        protocol.registerServerbound(ServerboundPackets1_13.SET_COMMAND_BLOCK, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.BLOCK_POSITION1_8, Types.BLOCK_POSITION1_14);\n            }\n        });\n        protocol.registerServerbound(ServerboundPackets1_13.SET_STRUCTURE_BLOCK, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.BLOCK_POSITION1_8, Types.BLOCK_POSITION1_14);\n            }\n        });\n        protocol.registerServerbound(ServerboundPackets1_13.SIGN_UPDATE, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.BLOCK_POSITION1_8, Types.BLOCK_POSITION1_14);\n            }\n        });\n\n        protocol.registerServerbound(ServerboundPackets1_13.USE_ITEM_ON, wrapper -> {\n            BlockPosition position = wrapper.read(Types.BLOCK_POSITION1_8);\n            int face = wrapper.read(Types.VAR_INT);\n            int hand = wrapper.read(Types.VAR_INT);\n            float x = wrapper.read(Types.FLOAT);\n            float y = wrapper.read(Types.FLOAT);\n            float z = wrapper.read(Types.FLOAT);\n\n            wrapper.write(Types.VAR_INT, hand);\n            wrapper.write(Types.BLOCK_POSITION1_14, position);\n            wrapper.write(Types.VAR_INT, face);\n            wrapper.write(Types.FLOAT, x);\n            wrapper.write(Types.FLOAT, y);\n            wrapper.write(Types.FLOAT, z);\n            wrapper.write(Types.BOOLEAN, false); // Inside block\n        });\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_14to1_13_2/rewriter/SoundPacketRewriter1_14.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_14to1_13_2.rewriter;\n\nimport com.viaversion.viabackwards.protocol.v1_14to1_13_2.Protocol1_14To1_13_2;\nimport com.viaversion.viabackwards.protocol.v1_14to1_13_2.storage.EntityPositionStorage1_14;\nimport com.viaversion.viaversion.api.data.entity.StoredEntityData;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.rewriter.RewriterBase;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.protocols.v1_12_2to1_13.packet.ClientboundPackets1_13;\nimport com.viaversion.viaversion.protocols.v1_13_2to1_14.packet.ClientboundPackets1_14;\n\npublic class SoundPacketRewriter1_14 extends RewriterBase<Protocol1_14To1_13_2> {\n\n    public SoundPacketRewriter1_14(Protocol1_14To1_13_2 protocol) {\n        super(protocol);\n    }\n\n    @Override\n    protected void registerPackets() {\n        // Entity Sound Effect\n        protocol.registerClientbound(ClientboundPackets1_14.SOUND_ENTITY, null, wrapper -> {\n            wrapper.cancel();\n\n            int soundId = wrapper.read(Types.VAR_INT);\n            int newId = protocol.getMappingData().getSoundMappings().getNewId(soundId);\n            if (newId == -1) return;\n\n            int category = wrapper.read(Types.VAR_INT);\n            int entityId = wrapper.read(Types.VAR_INT);\n\n            StoredEntityData storedEntity = wrapper.user().getEntityTracker(protocol.getClass()).entityData(entityId);\n            EntityPositionStorage1_14 entityStorage;\n            if (storedEntity == null || (entityStorage = storedEntity.get(EntityPositionStorage1_14.class)) == null) {\n                protocol.getLogger().warning(\"Untracked entity with id \" + entityId);\n                return;\n            }\n\n            float volume = wrapper.read(Types.FLOAT);\n            float pitch = wrapper.read(Types.FLOAT);\n            int x = (int) (entityStorage.x() * 8D);\n            int y = (int) (entityStorage.y() * 8D);\n            int z = (int) (entityStorage.z() * 8D);\n\n            PacketWrapper soundPacket = wrapper.create(ClientboundPackets1_13.SOUND);\n            soundPacket.write(Types.VAR_INT, newId);\n            soundPacket.write(Types.VAR_INT, category);\n            soundPacket.write(Types.INT, x);\n            soundPacket.write(Types.INT, y);\n            soundPacket.write(Types.INT, z);\n            soundPacket.write(Types.FLOAT, volume);\n            soundPacket.write(Types.FLOAT, pitch);\n            soundPacket.send(Protocol1_14To1_13_2.class);\n        });\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_14to1_13_2/storage/ChunkLightStorage.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_14to1_13_2.storage;\n\nimport com.viaversion.viaversion.api.connection.StorableObject;\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.InvocationTargetException;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.Map;\n\npublic class ChunkLightStorage implements StorableObject {\n    public static final byte[] FULL_LIGHT = new byte[2048];\n    public static final byte[] EMPTY_LIGHT = new byte[2048];\n    private static Constructor<?> fastUtilLongObjectHashMap;\n\n    private final Map<Long, ChunkLight> storedLight = createLongObjectMap();\n\n    static {\n        Arrays.fill(FULL_LIGHT, (byte) 0xFF);\n        Arrays.fill(EMPTY_LIGHT, (byte) 0x0);\n        try {\n            fastUtilLongObjectHashMap = Class.forName(\"it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap\").getConstructor();\n        } catch (ClassNotFoundException | NoSuchMethodException ignored) {\n        }\n    }\n\n    public void setStoredLight(byte[][] skyLight, byte[][] blockLight, int x, int z) {\n        storedLight.put(getChunkSectionIndex(x, z), new ChunkLight(skyLight, blockLight));\n    }\n\n    public ChunkLight getStoredLight(int x, int z) {\n        return storedLight.get(getChunkSectionIndex(x, z));\n    }\n\n    public void clear() {\n        storedLight.clear();\n    }\n\n    public void unloadChunk(int x, int z) {\n        storedLight.remove(getChunkSectionIndex(x, z));\n    }\n\n    private long getChunkSectionIndex(int x, int z) {\n        return ((x & 0x3FFFFFFL) << 38) | (z & 0x3FFFFFFL);\n    }\n\n    private Map<Long, ChunkLight> createLongObjectMap() {\n        if (fastUtilLongObjectHashMap != null) {\n            try {\n                return (Map<Long, ChunkLight>) fastUtilLongObjectHashMap.newInstance();\n            } catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {\n                e.printStackTrace();\n            }\n        }\n        return new HashMap<>();\n    }\n\n    public record ChunkLight(byte[][] skyLight, byte[][] blockLight) {\n\n        @Override\n        public boolean equals(final Object o) {\n            if (this == o) return true;\n            if (o == null || getClass() != o.getClass()) return false;\n            final ChunkLight that = (ChunkLight) o;\n            if (!Arrays.deepEquals(skyLight, that.skyLight)) return false;\n            return Arrays.deepEquals(blockLight, that.blockLight);\n        }\n\n        @Override\n        public int hashCode() {\n            int result = Arrays.deepHashCode(skyLight);\n            result = 31 * result + Arrays.deepHashCode(blockLight);\n            return result;\n        }\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_14to1_13_2/storage/DifficultyStorage.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_14to1_13_2.storage;\n\nimport com.viaversion.viaversion.api.connection.StorableObject;\n\npublic class DifficultyStorage implements StorableObject {\n    private byte difficulty;\n\n    public byte getDifficulty() {\n        return difficulty;\n    }\n\n    public void setDifficulty(byte difficulty) {\n        this.difficulty = difficulty;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_14to1_13_2/storage/EntityPositionStorage1_14.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_14to1_13_2.storage;\n\nimport com.viaversion.viabackwards.api.entities.storage.EntityPositionStorage;\n\npublic class EntityPositionStorage1_14 extends EntityPositionStorage {\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_15_1to1_15/Protocol1_15_1To1_15.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_15_1to1_15;\n\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viaversion.protocols.v1_13_2to1_14.packet.ServerboundPackets1_14;\nimport com.viaversion.viaversion.protocols.v1_14_4to1_15.packet.ClientboundPackets1_15;\n\npublic class Protocol1_15_1To1_15 extends BackwardsProtocol<ClientboundPackets1_15, ClientboundPackets1_15, ServerboundPackets1_14, ServerboundPackets1_14> {\n\n    public Protocol1_15_1To1_15() {\n        super(ClientboundPackets1_15.class, ClientboundPackets1_15.class, ServerboundPackets1_14.class, ServerboundPackets1_14.class);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_15_2to1_15_1/Protocol1_15_2To1_15_1.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_15_2to1_15_1;\n\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viaversion.protocols.v1_13_2to1_14.packet.ServerboundPackets1_14;\nimport com.viaversion.viaversion.protocols.v1_14_4to1_15.packet.ClientboundPackets1_15;\n\npublic class Protocol1_15_2To1_15_1 extends BackwardsProtocol<ClientboundPackets1_15, ClientboundPackets1_15, ServerboundPackets1_14, ServerboundPackets1_14> {\n\n    public Protocol1_15_2To1_15_1() {\n        super(ClientboundPackets1_15.class, ClientboundPackets1_15.class, ServerboundPackets1_14.class, ServerboundPackets1_14.class);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_15to1_14_4/Protocol1_15To1_14_4.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_15to1_14_4;\n\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viabackwards.api.data.BackwardsMappingData;\nimport com.viaversion.viabackwards.api.rewriters.text.JsonNBTComponentRewriter;\nimport com.viaversion.viabackwards.protocol.v1_15to1_14_4.rewriter.BlockItemPacketRewriter1_15;\nimport com.viaversion.viabackwards.protocol.v1_15to1_14_4.rewriter.EntityPacketRewriter1_15;\nimport com.viaversion.viabackwards.protocol.v1_15to1_14_4.storage.ImmediateRespawnStorage;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.ClientWorld;\nimport com.viaversion.viaversion.api.minecraft.RegistryType;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_15;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.data.entity.EntityTrackerBase;\nimport com.viaversion.viaversion.protocols.v1_13_2to1_14.packet.ServerboundPackets1_14;\nimport com.viaversion.viaversion.protocols.v1_14_3to1_14_4.packet.ClientboundPackets1_14_4;\nimport com.viaversion.viaversion.protocols.v1_14_4to1_15.Protocol1_14_4To1_15;\nimport com.viaversion.viaversion.protocols.v1_14_4to1_15.packet.ClientboundPackets1_15;\nimport com.viaversion.viaversion.rewriter.BlockRewriter;\nimport com.viaversion.viaversion.rewriter.ParticleRewriter;\nimport com.viaversion.viaversion.rewriter.TagRewriter;\nimport com.viaversion.viaversion.rewriter.text.ComponentRewriterBase;\n\npublic class Protocol1_15To1_14_4 extends BackwardsProtocol<ClientboundPackets1_15, ClientboundPackets1_14_4, ServerboundPackets1_14, ServerboundPackets1_14> {\n\n    public static final BackwardsMappingData MAPPINGS = new BackwardsMappingData(\"1.15\", \"1.14\", Protocol1_14_4To1_15.class);\n    private final EntityPacketRewriter1_15 entityRewriter = new EntityPacketRewriter1_15(this);\n    private final BlockItemPacketRewriter1_15 blockItemPackets = new BlockItemPacketRewriter1_15(this);\n    private final BlockRewriter<ClientboundPackets1_15> blockRewriter = BlockRewriter.for1_14(this);\n    private final ParticleRewriter<ClientboundPackets1_15> particleRewriter = new ParticleRewriter<>(this);\n    private final JsonNBTComponentRewriter<ClientboundPackets1_15> translatableRewriter = new JsonNBTComponentRewriter<>(this, ComponentRewriterBase.ReadType.JSON);\n    private final TagRewriter<ClientboundPackets1_15> tagRewriter = new TagRewriter<>(this);\n\n    public Protocol1_15To1_14_4() {\n        super(ClientboundPackets1_15.class, ClientboundPackets1_14_4.class, ServerboundPackets1_14.class, ServerboundPackets1_14.class);\n    }\n\n    @Override\n    protected void registerPackets() {\n        super.registerPackets();\n\n        // Explosion - manually send an explosion sound\n        registerClientbound(ClientboundPackets1_15.EXPLODE, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.FLOAT); // x\n                map(Types.FLOAT); // y\n                map(Types.FLOAT); // z\n                handler(wrapper -> {\n                    PacketWrapper soundPacket = wrapper.create(ClientboundPackets1_14_4.SOUND);\n                    soundPacket.write(Types.VAR_INT, 243); // entity.generic.explode\n                    soundPacket.write(Types.VAR_INT, 4); // blocks category\n                    soundPacket.write(Types.INT, toEffectCoordinate(wrapper.get(Types.FLOAT, 0))); // x\n                    soundPacket.write(Types.INT, toEffectCoordinate(wrapper.get(Types.FLOAT, 1))); // y\n                    soundPacket.write(Types.INT, toEffectCoordinate(wrapper.get(Types.FLOAT, 2))); // z\n                    soundPacket.write(Types.FLOAT, 4F); // volume\n                    soundPacket.write(Types.FLOAT, 1F); // pitch - usually semi randomized by the server, but we don't really have to care about that\n                    soundPacket.send(Protocol1_15To1_14_4.class);\n                });\n            }\n\n            private int toEffectCoordinate(float coordinate) {\n                return (int) (coordinate * 8);\n            }\n        });\n\n        tagRewriter.register(ClientboundPackets1_15.UPDATE_TAGS, RegistryType.ENTITY);\n    }\n\n    @Override\n    public void init(UserConnection user) {\n        user.addEntityTracker(getClass(), new EntityTrackerBase(user, EntityTypes1_15.PLAYER));\n        user.addClientWorld(getClass(), new ClientWorld());\n\n        user.put(new ImmediateRespawnStorage());\n    }\n\n    @Override\n    public BackwardsMappingData getMappingData() {\n        return MAPPINGS;\n    }\n\n    @Override\n    public EntityPacketRewriter1_15 getEntityRewriter() {\n        return entityRewriter;\n    }\n\n    @Override\n    public BlockItemPacketRewriter1_15 getItemRewriter() {\n        return blockItemPackets;\n    }\n\n    @Override\n    public BlockRewriter<ClientboundPackets1_15> getBlockRewriter() {\n        return blockRewriter;\n    }\n\n    @Override\n    public ParticleRewriter<ClientboundPackets1_15> getParticleRewriter() {\n        return particleRewriter;\n    }\n\n    @Override\n    public TagRewriter<ClientboundPackets1_15> getTagRewriter() {\n        return tagRewriter;\n    }\n\n    @Override\n    public JsonNBTComponentRewriter<ClientboundPackets1_15> getComponentRewriter() {\n        return translatableRewriter;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_15to1_14_4/rewriter/BlockItemPacketRewriter1_15.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_15to1_14_4.rewriter;\n\nimport com.viaversion.viabackwards.api.rewriters.BackwardsItemRewriter;\nimport com.viaversion.viabackwards.protocol.v1_15to1_14_4.Protocol1_15To1_14_4;\nimport com.viaversion.viaversion.api.minecraft.chunks.Chunk;\nimport com.viaversion.viaversion.api.minecraft.item.Item;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.chunk.ChunkType1_14;\nimport com.viaversion.viaversion.api.type.types.chunk.ChunkType1_15;\nimport com.viaversion.viaversion.protocols.v1_13_2to1_14.packet.ServerboundPackets1_14;\nimport com.viaversion.viaversion.protocols.v1_14_4to1_15.packet.ClientboundPackets1_15;\nimport com.viaversion.viaversion.rewriter.RecipeRewriter;\n\npublic class BlockItemPacketRewriter1_15 extends BackwardsItemRewriter<ClientboundPackets1_15, ServerboundPackets1_14, Protocol1_15To1_14_4> {\n\n    public BlockItemPacketRewriter1_15(Protocol1_15To1_14_4 protocol) {\n        super(protocol, Types.ITEM1_13_2, Types.ITEM1_13_2_SHORT_ARRAY);\n    }\n\n    @Override\n    protected void registerPackets() {\n        new RecipeRewriter<>(protocol).register(ClientboundPackets1_15.UPDATE_RECIPES);\n\n        protocol.registerServerbound(ServerboundPackets1_14.EDIT_BOOK, wrapper -> handleItemToServer(wrapper.user(), wrapper.passthrough(Types.ITEM1_13_2)));\n\n        protocol.registerClientbound(ClientboundPackets1_15.LEVEL_CHUNK, wrapper -> {\n            Chunk chunk = wrapper.read(ChunkType1_15.TYPE);\n            wrapper.write(ChunkType1_14.TYPE, chunk);\n\n            if (chunk.isFullChunk()) {\n                int[] biomeData = chunk.getBiomeData();\n                int[] newBiomeData = new int[256];\n                for (int i = 0; i < 4; ++i) {\n                    for (int j = 0; j < 4; ++j) {\n                        int x = j << 2;\n                        int z = i << 2;\n                        int newIndex = z << 4 | x;\n                        int oldIndex = i << 2 | j;\n\n                        int biome = biomeData[oldIndex];\n                        for (int k = 0; k < 4; k++) {\n                            int offX = newIndex + (k << 4);\n                            for (int l = 0; l < 4; l++) {\n                                newBiomeData[offX + l] = biome;\n                            }\n                        }\n                    }\n                }\n\n                chunk.setBiomeData(newBiomeData);\n            }\n\n            protocol.getBlockRewriter().handleChunk(chunk);\n        });\n\n        protocol.replaceClientbound(ClientboundPackets1_15.LEVEL_PARTICLES, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.INT); // 0 - Particle ID\n                map(Types.BOOLEAN); // 1 - Long Distance\n                map(Types.DOUBLE, Types.FLOAT); // 2 - X\n                map(Types.DOUBLE, Types.FLOAT); // 3 - Y\n                map(Types.DOUBLE, Types.FLOAT); // 4 - Z\n                map(Types.FLOAT); // 5 - Offset X\n                map(Types.FLOAT); // 6 - Offset Y\n                map(Types.FLOAT); // 7 - Offset Z\n                map(Types.FLOAT); // 8 - Particle Data\n                map(Types.INT); // 9 - Particle Count\n                handler(wrapper -> {\n                    int id = wrapper.get(Types.INT, 0);\n                    if (id == 3 || id == 23) {\n                        int data = wrapper.passthrough(Types.VAR_INT);\n                        wrapper.set(Types.VAR_INT, 0, protocol.getMappingData().getNewBlockStateId(data));\n                    } else if (id == 32) {\n                        Item item = handleItemToClient(wrapper.user(), wrapper.read(Types.ITEM1_13_2));\n                        wrapper.write(Types.ITEM1_13_2, item);\n                    }\n\n                    int mappedId = protocol.getMappingData().getNewParticleId(id);\n                    if (id != mappedId) {\n                        wrapper.set(Types.INT, 0, mappedId);\n                    }\n                });\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_15to1_14_4/rewriter/EntityPacketRewriter1_15.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_15to1_14_4.rewriter;\n\nimport com.viaversion.viabackwards.api.rewriters.EntityRewriter;\nimport com.viaversion.viabackwards.protocol.v1_15to1_14_4.Protocol1_15To1_14_4;\nimport com.viaversion.viabackwards.protocol.v1_15to1_14_4.storage.ImmediateRespawnStorage;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityType;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_15;\nimport com.viaversion.viaversion.api.minecraft.entitydata.EntityData;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.version.Types1_14;\nimport com.viaversion.viaversion.protocols.v1_13_2to1_14.packet.ServerboundPackets1_14;\nimport com.viaversion.viaversion.protocols.v1_14_4to1_15.packet.ClientboundPackets1_15;\nimport java.util.ArrayList;\n\npublic class EntityPacketRewriter1_15 extends EntityRewriter<ClientboundPackets1_15, Protocol1_15To1_14_4> {\n\n    public EntityPacketRewriter1_15(Protocol1_15To1_14_4 protocol) {\n        super(protocol);\n    }\n\n    @Override\n    protected void registerPackets() {\n        protocol.registerClientbound(ClientboundPackets1_15.SET_HEALTH, wrapper -> {\n            float health = wrapper.passthrough(Types.FLOAT);\n            if (health > 0) return;\n            if (!wrapper.user().get(ImmediateRespawnStorage.class).isImmediateRespawn()) return;\n\n            // Instantly request respawn when 1.15 gamerule is set\n            PacketWrapper statusPacket = wrapper.create(ServerboundPackets1_14.CLIENT_COMMAND);\n            statusPacket.write(Types.VAR_INT, 0);\n            statusPacket.sendToServer(Protocol1_15To1_14_4.class);\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_15.GAME_EVENT, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.UNSIGNED_BYTE);\n                map(Types.FLOAT);\n                handler(wrapper -> {\n                    if (wrapper.get(Types.UNSIGNED_BYTE, 0) == 11) {\n                        wrapper.user().get(ImmediateRespawnStorage.class).setImmediateRespawn(wrapper.get(Types.FLOAT, 0) == 1);\n                    }\n                });\n            }\n        });\n\n        protocol.replaceClientbound(ClientboundPackets1_15.ADD_MOB, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT); // 0 - Entity ID\n                map(Types.UUID); // 1 - Entity UUID\n                map(Types.VAR_INT); // 2 - Entity Type\n                map(Types.DOUBLE); // 3 - X\n                map(Types.DOUBLE); // 4 - Y\n                map(Types.DOUBLE); // 5 - Z\n                map(Types.BYTE); // 6 - Yaw\n                map(Types.BYTE); // 7 - Pitch\n                map(Types.BYTE); // 8 - Head Pitch\n                map(Types.SHORT); // 9 - Velocity X\n                map(Types.SHORT); // 10 - Velocity Y\n                map(Types.SHORT); // 11 - Velocity Z\n                handler(wrapper -> wrapper.write(Types1_14.ENTITY_DATA_LIST, new ArrayList<>())); // Entity data is no longer sent in 1.15, so we have to send an empty one\n\n                handler(wrapper -> {\n                    int type = wrapper.get(Types.VAR_INT, 1);\n                    EntityType entityType = EntityTypes1_15.getTypeFromId(type);\n                    tracker(wrapper.user()).addEntity(wrapper.get(Types.VAR_INT, 0), entityType);\n                    wrapper.set(Types.VAR_INT, 1, newEntityId(type));\n                });\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_15.RESPAWN, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.INT);\n                read(Types.LONG); // Seed\n                handler(getDimensionHandler(0));\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_15.LOGIN, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.INT); // 0 - Entity ID\n                map(Types.UNSIGNED_BYTE); // 1 - Gamemode\n                map(Types.INT); // 2 - Dimension\n                handler(getDimensionHandler(1));\n\n                read(Types.LONG); // Seed\n\n                map(Types.UNSIGNED_BYTE); // 3 - Max Players\n                map(Types.STRING); // 4 - Level Type\n                map(Types.VAR_INT); // 5 - View Distance\n                map(Types.BOOLEAN); // 6 - Reduce Debug Info\n\n                handler(getPlayerTrackerHandler());\n\n                handler(wrapper -> {\n                    boolean immediateRespawn = !wrapper.read(Types.BOOLEAN); // Inverted\n                    wrapper.user().get(ImmediateRespawnStorage.class).setImmediateRespawn(immediateRespawn);\n                });\n            }\n        });\n\n        registerTracker(ClientboundPackets1_15.ADD_EXPERIENCE_ORB, EntityTypes1_15.EXPERIENCE_ORB);\n        registerTracker(ClientboundPackets1_15.ADD_GLOBAL_ENTITY, EntityTypes1_15.LIGHTNING_BOLT);\n        registerTracker(ClientboundPackets1_15.ADD_PAINTING, EntityTypes1_15.PAINTING);\n\n        protocol.registerClientbound(ClientboundPackets1_15.ADD_PLAYER, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT); // 0 - Entity ID\n                map(Types.UUID); // 1 - Player UUID\n                map(Types.DOUBLE); // 2 - X\n                map(Types.DOUBLE); // 3 - Y\n                map(Types.DOUBLE); // 4 - Z\n                map(Types.BYTE); // 5 - Yaw\n                map(Types.BYTE); // 6 - Pitch\n                handler(wrapper -> wrapper.write(Types1_14.ENTITY_DATA_LIST, new ArrayList<>())); // Entity data is no longer sent in 1.15, so we have to send an empty one\n\n                handler(getTrackerHandler(EntityTypes1_15.PLAYER));\n            }\n        });\n\n        registerSetEntityData(ClientboundPackets1_15.SET_ENTITY_DATA, Types1_14.ENTITY_DATA_LIST);\n\n        // Attributes (get rid of generic.flyingSpeed for the Bee remap)\n        protocol.registerClientbound(ClientboundPackets1_15.UPDATE_ATTRIBUTES, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT);\n                map(Types.INT);\n                handler(wrapper -> {\n                    int entityId = wrapper.get(Types.VAR_INT, 0);\n                    EntityType entityType = tracker(wrapper.user()).entityType(entityId);\n                    if (entityType != EntityTypes1_15.BEE) return;\n\n                    int size = wrapper.get(Types.INT, 0);\n                    int newSize = size;\n                    for (int i = 0; i < size; i++) {\n                        String key = wrapper.read(Types.STRING);\n                        if (key.equals(\"generic.flyingSpeed\")) {\n                            newSize--;\n                            wrapper.read(Types.DOUBLE);\n                            int modSize = wrapper.read(Types.VAR_INT);\n                            for (int j = 0; j < modSize; j++) {\n                                wrapper.read(Types.UUID);\n                                wrapper.read(Types.DOUBLE);\n                                wrapper.read(Types.BYTE);\n                            }\n                        } else {\n                            wrapper.write(Types.STRING, key);\n                            wrapper.passthrough(Types.DOUBLE);\n                            int modSize = wrapper.passthrough(Types.VAR_INT);\n                            for (int j = 0; j < modSize; j++) {\n                                wrapper.passthrough(Types.UUID);\n                                wrapper.passthrough(Types.DOUBLE);\n                                wrapper.passthrough(Types.BYTE);\n                            }\n                        }\n                    }\n\n                    if (newSize != size) {\n                        wrapper.set(Types.INT, 0, newSize);\n                    }\n                });\n            }\n        });\n    }\n\n    @Override\n    protected void registerRewrites() {\n        registerEntityDataTypeHandler(Types1_14.ENTITY_DATA_TYPES.itemType, null, Types1_14.ENTITY_DATA_TYPES.optionalBlockStateType, Types1_14.ENTITY_DATA_TYPES.particleType,\n            Types1_14.ENTITY_DATA_TYPES.componentType, Types1_14.ENTITY_DATA_TYPES.optionalComponentType);\n\n        filter().type(EntityTypes1_15.LIVING_ENTITY).removeIndex(12);\n\n        filter().type(EntityTypes1_15.BEE).cancel(15);\n        filter().type(EntityTypes1_15.BEE).cancel(16);\n\n        filter().type(EntityTypes1_15.ENDERMAN).cancel(16);\n        filter().type(EntityTypes1_15.TRIDENT).cancel(10);\n\n        // Redundant health removed in 1.15\n        filter().type(EntityTypes1_15.WOLF).addIndex(17);\n        filter().type(EntityTypes1_15.WOLF).index(8).handler((event, data) -> {\n            event.createExtraData(new EntityData(17/*WOLF_HEALTH*/, Types1_14.ENTITY_DATA_TYPES.floatType, event.data().value()));\n        });\n    }\n\n    @Override\n    public void onMappingDataLoaded() {\n        super.onMappingDataLoaded();\n        mapEntityTypeWithData(EntityTypes1_15.BEE, EntityTypes1_15.PUFFERFISH).jsonName().spawnEntityData(storage -> {\n            storage.add(new EntityData(14, Types1_14.ENTITY_DATA_TYPES.booleanType, false));\n            storage.add(new EntityData(15, Types1_14.ENTITY_DATA_TYPES.varIntType, 2));\n        });\n    }\n\n    @Override\n    public EntityType typeFromId(int typeId) {\n        return EntityTypes1_15.getTypeFromId(typeId);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_15to1_14_4/storage/ImmediateRespawnStorage.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_15to1_14_4.storage;\n\nimport com.viaversion.viaversion.api.connection.StorableObject;\n\npublic class ImmediateRespawnStorage implements StorableObject {\n    private boolean immediateRespawn;\n\n    public boolean isImmediateRespawn() {\n        return immediateRespawn;\n    }\n\n    public void setImmediateRespawn(boolean immediateRespawn) {\n        this.immediateRespawn = immediateRespawn;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_16_1to1_16/Protocol1_16_1To1_16.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_16_1to1_16;\n\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viaversion.protocols.v1_15_2to1_16.packet.ClientboundPackets1_16;\nimport com.viaversion.viaversion.protocols.v1_15_2to1_16.packet.ServerboundPackets1_16;\n\npublic class Protocol1_16_1To1_16 extends BackwardsProtocol<ClientboundPackets1_16, ClientboundPackets1_16, ServerboundPackets1_16, ServerboundPackets1_16> {\n\n    public Protocol1_16_1To1_16() {\n        super(ClientboundPackets1_16.class, ClientboundPackets1_16.class, ServerboundPackets1_16.class, ServerboundPackets1_16.class);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_16_2to1_16_1/Protocol1_16_2To1_16_1.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_16_2to1_16_1;\n\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viabackwards.api.data.BackwardsMappingData;\nimport com.viaversion.viabackwards.api.rewriters.text.JsonNBTComponentRewriter;\nimport com.viaversion.viabackwards.protocol.v1_16_2to1_16_1.rewriter.BlockItemPacketRewriter1_16_2;\nimport com.viaversion.viabackwards.protocol.v1_16_2to1_16_1.rewriter.CommandRewriter1_16_2;\nimport com.viaversion.viabackwards.protocol.v1_16_2to1_16_1.rewriter.EntityPacketRewriter1_16_2;\nimport com.viaversion.viabackwards.protocol.v1_16_2to1_16_1.storage.BiomeStorage;\nimport com.viaversion.viabackwards.utils.BackwardsProtocolLogger;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.RegistryType;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_16_2;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.data.entity.EntityTrackerBase;\nimport com.viaversion.viaversion.libs.gson.JsonElement;\nimport com.viaversion.viaversion.protocols.v1_15_2to1_16.packet.ClientboundPackets1_16;\nimport com.viaversion.viaversion.protocols.v1_15_2to1_16.packet.ServerboundPackets1_16;\nimport com.viaversion.viaversion.protocols.v1_16_1to1_16_2.Protocol1_16_1To1_16_2;\nimport com.viaversion.viaversion.protocols.v1_16_1to1_16_2.packet.ClientboundPackets1_16_2;\nimport com.viaversion.viaversion.protocols.v1_16_1to1_16_2.packet.ServerboundPackets1_16_2;\nimport com.viaversion.viaversion.rewriter.BlockRewriter;\nimport com.viaversion.viaversion.rewriter.ParticleRewriter;\nimport com.viaversion.viaversion.rewriter.TagRewriter;\nimport com.viaversion.viaversion.rewriter.text.ComponentRewriterBase;\nimport com.viaversion.viaversion.util.ProtocolLogger;\n\npublic class Protocol1_16_2To1_16_1 extends BackwardsProtocol<ClientboundPackets1_16_2, ClientboundPackets1_16, ServerboundPackets1_16_2, ServerboundPackets1_16> {\n\n    public static final BackwardsMappingData MAPPINGS = new BackwardsMappingData(\"1.16.2\", \"1.16\", Protocol1_16_1To1_16_2.class);\n    public static final ProtocolLogger LOGGER = new BackwardsProtocolLogger(Protocol1_16_2To1_16_1.class);\n    private final EntityPacketRewriter1_16_2 entityRewriter = new EntityPacketRewriter1_16_2(this);\n    private final BlockItemPacketRewriter1_16_2 blockItemPackets = new BlockItemPacketRewriter1_16_2(this);\n    private final BlockRewriter<ClientboundPackets1_16_2> blockRewriter = BlockRewriter.for1_14(this);\n    private final ParticleRewriter<ClientboundPackets1_16_2> particleRewriter = new ParticleRewriter<>(this);\n    private final JsonNBTComponentRewriter<ClientboundPackets1_16_2> translatableRewriter = new JsonNBTComponentRewriter<>(this, ComponentRewriterBase.ReadType.JSON);\n    private final TagRewriter<ClientboundPackets1_16_2> tagRewriter = new TagRewriter<>(this);\n\n    public Protocol1_16_2To1_16_1() {\n        super(ClientboundPackets1_16_2.class, ClientboundPackets1_16.class, ServerboundPackets1_16_2.class, ServerboundPackets1_16.class);\n    }\n\n    @Override\n    protected void registerPackets() {\n        super.registerPackets();\n\n        new CommandRewriter1_16_2(this).registerDeclareCommands(ClientboundPackets1_16_2.COMMANDS);\n\n        replaceClientbound(ClientboundPackets1_16_2.CHAT, wrapper -> {\n            JsonElement message = wrapper.passthrough(Types.COMPONENT);\n            translatableRewriter.processText(wrapper.user(), message);\n            byte position = wrapper.passthrough(Types.BYTE);\n            if (position == 2) { // https://bugs.mojang.com/browse/MC-119145\n                wrapper.clearPacket();\n                wrapper.setPacketType(ClientboundPackets1_16.SET_TITLES);\n                wrapper.write(Types.VAR_INT, 2);\n                wrapper.write(Types.COMPONENT, message);\n            }\n        });\n\n        // Recipe book data has been split into 2 separate packets\n        registerServerbound(ServerboundPackets1_16.RECIPE_BOOK_UPDATE, ServerboundPackets1_16_2.RECIPE_BOOK_CHANGE_SETTINGS, wrapper -> {\n            int type = wrapper.read(Types.VAR_INT);\n            if (type == 0) {\n                // Shown, change to its own packet\n                wrapper.passthrough(Types.STRING); // Recipe\n                wrapper.setPacketType(ServerboundPackets1_16_2.RECIPE_BOOK_SEEN_RECIPE);\n            } else {\n                wrapper.cancel();\n\n                // Settings\n                for (int i = 0; i < 3; i++) {\n                    sendSeenRecipePacket(i, wrapper);\n                }\n            }\n        });\n\n        tagRewriter.register(ClientboundPackets1_16_2.UPDATE_TAGS, RegistryType.ENTITY);\n    }\n\n    private static void sendSeenRecipePacket(int recipeType, PacketWrapper wrapper) {\n        boolean open = wrapper.read(Types.BOOLEAN);\n        boolean filter = wrapper.read(Types.BOOLEAN);\n\n        PacketWrapper newPacket = wrapper.create(ServerboundPackets1_16_2.RECIPE_BOOK_CHANGE_SETTINGS);\n        newPacket.write(Types.VAR_INT, recipeType);\n        newPacket.write(Types.BOOLEAN, open);\n        newPacket.write(Types.BOOLEAN, filter);\n        newPacket.sendToServer(Protocol1_16_2To1_16_1.class);\n    }\n\n    @Override\n    public void init(UserConnection user) {\n        user.put(new BiomeStorage());\n        user.addEntityTracker(this.getClass(), new EntityTrackerBase(user, EntityTypes1_16_2.PLAYER));\n    }\n\n    @Override\n    public JsonNBTComponentRewriter<ClientboundPackets1_16_2> getComponentRewriter() {\n        return translatableRewriter;\n    }\n\n    @Override\n    public BackwardsMappingData getMappingData() {\n        return MAPPINGS;\n    }\n\n    @Override\n    public ProtocolLogger getLogger() {\n        return LOGGER;\n    }\n\n    @Override\n    public EntityPacketRewriter1_16_2 getEntityRewriter() {\n        return entityRewriter;\n    }\n\n    @Override\n    public BlockItemPacketRewriter1_16_2 getItemRewriter() {\n        return blockItemPackets;\n    }\n\n    @Override\n    public BlockRewriter<ClientboundPackets1_16_2> getBlockRewriter() {\n        return blockRewriter;\n    }\n\n    @Override\n    public ParticleRewriter<ClientboundPackets1_16_2> getParticleRewriter() {\n        return particleRewriter;\n    }\n\n    @Override\n    public TagRewriter<ClientboundPackets1_16_2> getTagRewriter() {\n        return tagRewriter;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_16_2to1_16_1/data/BiomeMappings1_16_1.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_16_2to1_16_1.data;\n\nimport com.viaversion.viabackwards.api.data.BackwardsMappingDataLoader;\nimport com.viaversion.viabackwards.protocol.v1_16_2to1_16_1.Protocol1_16_2To1_16_1;\nimport com.viaversion.viaversion.api.Via;\nimport com.viaversion.viaversion.libs.fastutil.objects.Object2IntMap;\nimport com.viaversion.viaversion.libs.fastutil.objects.Object2IntOpenHashMap;\nimport com.viaversion.viaversion.libs.gson.JsonElement;\nimport com.viaversion.viaversion.libs.gson.JsonObject;\nimport com.viaversion.viaversion.util.Key;\nimport java.util.Map;\n\npublic final class BiomeMappings1_16_1 {\n\n    private static final Object2IntMap<String> MODERN_TO_LEGACY_ID = new Object2IntOpenHashMap<>();\n    private static final Object2IntMap<String> LEGACY_BIOMES = new Object2IntOpenHashMap<>();\n\n    static {\n        LEGACY_BIOMES.defaultReturnValue(-1);\n        MODERN_TO_LEGACY_ID.defaultReturnValue(-1);\n\n        add(0, \"ocean\");\n        add(1, \"plains\");\n        add(2, \"desert\");\n        add(3, \"mountains\");\n        add(4, \"forest\");\n        add(5, \"taiga\");\n        add(6, \"swamp\");\n        add(7, \"river\");\n        add(8, \"nether\");\n        add(9, \"the_end\");\n        add(10, \"frozen_ocean\");\n        add(11, \"frozen_river\");\n        add(12, \"snowy_tundra\");\n        add(13, \"snowy_mountains\");\n        add(14, \"mushroom_fields\");\n        add(15, \"mushroom_field_shore\");\n        add(16, \"beach\");\n        add(17, \"desert_hills\");\n        add(18, \"wooded_hills\");\n        add(19, \"taiga_hills\");\n        add(20, \"mountain_edge\");\n        add(21, \"jungle\");\n        add(22, \"jungle_hills\");\n        add(23, \"jungle_edge\");\n        add(24, \"deep_ocean\");\n        add(25, \"stone_shore\");\n        add(26, \"snowy_beach\");\n        add(27, \"birch_forest\");\n        add(28, \"birch_forest_hills\");\n        add(29, \"dark_forest\");\n        add(30, \"snowy_taiga\");\n        add(31, \"snowy_taiga_hills\");\n        add(32, \"giant_tree_taiga\");\n        add(33, \"giant_tree_taiga_hills\");\n        add(34, \"wooded_mountains\");\n        add(35, \"savanna\");\n        add(36, \"savanna_plateau\");\n        add(37, \"badlands\");\n        add(38, \"wooded_badlands_plateau\");\n        add(39, \"badlands_plateau\");\n        add(40, \"small_end_islands\");\n        add(41, \"end_midlands\");\n        add(42, \"end_highlands\");\n        add(43, \"end_barrens\");\n        add(44, \"warm_ocean\");\n        add(45, \"lukewarm_ocean\");\n        add(46, \"cold_ocean\");\n        add(47, \"deep_warm_ocean\");\n        add(48, \"deep_lukewarm_ocean\");\n        add(49, \"deep_cold_ocean\");\n        add(50, \"deep_frozen_ocean\");\n        add(127, \"the_void\");\n        add(129, \"sunflower_plains\");\n        add(130, \"desert_lakes\");\n        add(131, \"gravelly_mountains\");\n        add(132, \"flower_forest\");\n        add(133, \"taiga_mountains\");\n        add(134, \"swamp_hills\");\n        add(140, \"ice_spikes\");\n        add(149, \"modified_jungle\");\n        add(151, \"modified_jungle_edge\");\n        add(155, \"tall_birch_forest\");\n        add(156, \"tall_birch_hills\");\n        add(157, \"dark_forest_hills\");\n        add(158, \"snowy_taiga_mountains\");\n        add(160, \"giant_spruce_taiga\");\n        add(161, \"giant_spruce_taiga_hills\");\n        add(162, \"modified_gravelly_mountains\");\n        add(163, \"shattered_savanna\");\n        add(164, \"shattered_savanna_plateau\");\n        add(165, \"eroded_badlands\");\n        add(166, \"modified_wooded_badlands_plateau\");\n        add(167, \"modified_badlands_plateau\");\n        add(168, \"bamboo_jungle\");\n        add(169, \"bamboo_jungle_hills\");\n\n        // Include the legacy biomes themselves\n        for (final Object2IntMap.Entry<String> entry : LEGACY_BIOMES.object2IntEntrySet()) {\n            MODERN_TO_LEGACY_ID.put(entry.getKey(), entry.getIntValue());\n        }\n\n        final JsonObject mappings = BackwardsMappingDataLoader.INSTANCE.loadFromDataDir(\"biome-mappings.json\");\n        for (final Map.Entry<String, JsonElement> entry : mappings.entrySet()) {\n            final int legacyBiome = LEGACY_BIOMES.getInt(entry.getValue().getAsString());\n            if (legacyBiome == -1) {\n                Protocol1_16_2To1_16_1.LOGGER.warning(\"Unknown legacy biome: \" + entry.getValue().getAsString());\n                continue;\n            }\n\n            MODERN_TO_LEGACY_ID.put(entry.getKey(), legacyBiome);\n        }\n    }\n\n    private static void add(final int id, final String biome) {\n        LEGACY_BIOMES.put(biome, id);\n    }\n\n    public static int toLegacyBiome(String biome) {\n        final int legacyBiome = MODERN_TO_LEGACY_ID.getInt(Key.stripMinecraftNamespace(biome));\n        if (legacyBiome == -1) {\n            if (Via.getConfig().logOtherConversionWarnings()) {\n                Protocol1_16_2To1_16_1.LOGGER.warning(\"Biome with id \" + biome + \" has no legacy biome mapping (custom datapack?)\");\n            }\n            return 1; // Plains\n        }\n        return legacyBiome;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_16_2to1_16_1/rewriter/BlockItemPacketRewriter1_16_2.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_16_2to1_16_1.rewriter;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.IntArrayTag;\nimport com.viaversion.nbt.tag.ListTag;\nimport com.viaversion.viabackwards.api.rewriters.BackwardsItemRewriter;\nimport com.viaversion.viabackwards.protocol.v1_16_2to1_16_1.Protocol1_16_2To1_16_1;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.BlockChangeRecord;\nimport com.viaversion.viaversion.api.minecraft.BlockChangeRecord1_8;\nimport com.viaversion.viaversion.api.minecraft.item.Item;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.chunk.ChunkType1_16;\nimport com.viaversion.viaversion.api.type.types.chunk.ChunkType1_16_2;\nimport com.viaversion.viaversion.protocols.v1_15_2to1_16.packet.ClientboundPackets1_16;\nimport com.viaversion.viaversion.protocols.v1_15_2to1_16.packet.ServerboundPackets1_16;\nimport com.viaversion.viaversion.protocols.v1_16_1to1_16_2.packet.ClientboundPackets1_16_2;\nimport com.viaversion.viaversion.rewriter.RecipeRewriter;\nimport com.viaversion.viaversion.util.Key;\nimport org.checkerframework.checker.nullness.qual.Nullable;\n\npublic class BlockItemPacketRewriter1_16_2 extends BackwardsItemRewriter<ClientboundPackets1_16_2, ServerboundPackets1_16, Protocol1_16_2To1_16_1> {\n\n    public BlockItemPacketRewriter1_16_2(Protocol1_16_2To1_16_1 protocol) {\n        super(protocol, Types.ITEM1_13_2, Types.ITEM1_13_2_SHORT_ARRAY);\n    }\n\n    @Override\n    protected void registerPackets() {\n        new RecipeRewriter<>(protocol).register(ClientboundPackets1_16_2.UPDATE_RECIPES);\n\n        protocol.registerClientbound(ClientboundPackets1_16_2.RECIPE, wrapper -> {\n            wrapper.passthrough(Types.VAR_INT);\n            wrapper.passthrough(Types.BOOLEAN); // Open\n            wrapper.passthrough(Types.BOOLEAN); // Filter\n            wrapper.passthrough(Types.BOOLEAN); // Furnace Open\n            wrapper.passthrough(Types.BOOLEAN); // Filter furnace\n            // Blast furnace / smoker\n            wrapper.read(Types.BOOLEAN);\n            wrapper.read(Types.BOOLEAN);\n            wrapper.read(Types.BOOLEAN);\n            wrapper.read(Types.BOOLEAN);\n        });\n\n        protocol.getBlockRewriter().registerLevelChunk(ClientboundPackets1_16_2.LEVEL_CHUNK, ChunkType1_16_2.TYPE, ChunkType1_16.TYPE, (connection, chunk) -> {\n            chunk.setIgnoreOldLightData(true);\n\n            for (CompoundTag blockEntity : chunk.getBlockEntities()) {\n                if (blockEntity != null) {\n                    handleBlockEntity(blockEntity);\n                }\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_16_2.BLOCK_ENTITY_DATA, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.BLOCK_POSITION1_14);\n                map(Types.UNSIGNED_BYTE);\n                handler(wrapper -> handleBlockEntity(wrapper.passthrough(Types.NAMED_COMPOUND_TAG)));\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_16_2.SECTION_BLOCKS_UPDATE, ClientboundPackets1_16.CHUNK_BLOCKS_UPDATE, wrapper -> {\n            long chunkPosition = wrapper.read(Types.LONG);\n            wrapper.read(Types.BOOLEAN); // Ignore old light data\n\n            int chunkX = (int) (chunkPosition >> 42);\n            int chunkY = (int) (chunkPosition << 44 >> 44);\n            int chunkZ = (int) (chunkPosition << 22 >> 42);\n            wrapper.write(Types.INT, chunkX);\n            wrapper.write(Types.INT, chunkZ);\n\n            BlockChangeRecord[] blockChangeRecord = wrapper.read(Types.VAR_LONG_BLOCK_CHANGE_ARRAY);\n            wrapper.write(Types.BLOCK_CHANGE_ARRAY, blockChangeRecord);\n            for (int i = 0; i < blockChangeRecord.length; i++) {\n                BlockChangeRecord record = blockChangeRecord[i];\n                int blockId = protocol.getMappingData().getNewBlockStateId(record.getBlockId());\n                // Relative y -> absolute y\n                blockChangeRecord[i] = new BlockChangeRecord1_8(record.getSectionX(), record.getY(chunkY), record.getSectionZ(), blockId);\n            }\n        });\n\n        protocol.registerServerbound(ServerboundPackets1_16.EDIT_BOOK, wrapper -> handleItemToServer(wrapper.user(), wrapper.passthrough(Types.ITEM1_13_2)));\n    }\n\n    @Override\n    public @Nullable Item handleItemToClient(final UserConnection connection, @Nullable final Item item) {\n        if (item != null && item.tag() != null) {\n            addValueHashAsId(item.tag());\n        }\n        return super.handleItemToClient(connection, item);\n    }\n\n    private void handleBlockEntity(CompoundTag tag) {\n        String id = tag.getString(\"id\");\n        if (id != null && Key.stripMinecraftNamespace(id).equals(\"skull\")) {\n            addValueHashAsId(tag);\n        }\n    }\n\n    private void addValueHashAsId(CompoundTag tag) {\n        // Workaround an old client bug: MC-68487\n        CompoundTag skullOwnerTag = tag.getCompoundTag(\"SkullOwner\");\n        if (skullOwnerTag == null) return;\n\n        if (!skullOwnerTag.contains(\"Id\")) return;\n\n        CompoundTag properties = skullOwnerTag.getCompoundTag(\"Properties\");\n        if (properties == null) return;\n\n        ListTag<CompoundTag> textures = properties.getListTag(\"textures\", CompoundTag.class);\n        if (textures == null) return;\n\n        CompoundTag first = !textures.isEmpty() ? textures.get(0) : null;\n        if (first == null) return;\n\n        // Make the client cache the skinprofile over this uuid\n        int hashCode = first.get(\"Value\").getValue().hashCode();\n        int[] uuidIntArray = {hashCode, 0, 0, 0};\n        skullOwnerTag.put(\"Id\", new IntArrayTag(uuidIntArray));\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_16_2to1_16_1/rewriter/CommandRewriter1_16_2.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_16_2to1_16_1.rewriter;\n\nimport com.viaversion.viabackwards.protocol.v1_16_2to1_16_1.Protocol1_16_2To1_16_1;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.protocols.v1_16_1to1_16_2.packet.ClientboundPackets1_16_2;\nimport com.viaversion.viaversion.rewriter.CommandRewriter;\nimport org.checkerframework.checker.nullness.qual.Nullable;\n\npublic class CommandRewriter1_16_2 extends CommandRewriter<ClientboundPackets1_16_2> {\n\n    public CommandRewriter1_16_2(Protocol1_16_2To1_16_1 protocol) {\n        super(protocol);\n\n        this.parserHandlers.put(\"minecraft:angle\", wrapper -> wrapper.write(Types.VAR_INT, 0)); // Single word\n    }\n\n    @Override\n    public @Nullable String handleArgumentType(String argumentType) {\n        if (argumentType.equals(\"minecraft:angle\")) {\n            return \"brigadier:string\";\n        }\n        return super.handleArgumentType(argumentType);\n    }\n\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_16_2to1_16_1/rewriter/EntityPacketRewriter1_16_2.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_16_2to1_16_1.rewriter;\n\nimport com.google.common.collect.Sets;\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.ListTag;\nimport com.viaversion.nbt.tag.NumberTag;\nimport com.viaversion.nbt.tag.StringTag;\nimport com.viaversion.viabackwards.ViaBackwards;\nimport com.viaversion.viabackwards.api.rewriters.EntityRewriter;\nimport com.viaversion.viabackwards.protocol.v1_16_2to1_16_1.Protocol1_16_2To1_16_1;\nimport com.viaversion.viabackwards.protocol.v1_16_2to1_16_1.storage.BiomeStorage;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityType;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_16_2;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.protocol.version.ProtocolVersion;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.version.Types1_16;\nimport com.viaversion.viaversion.protocols.v1_15_2to1_16.data.DimensionRegistries1_16;\nimport com.viaversion.viaversion.protocols.v1_16_1to1_16_2.packet.ClientboundPackets1_16_2;\nimport com.viaversion.viaversion.util.Key;\nimport com.viaversion.viaversion.util.TagUtil;\nimport java.util.Set;\n\npublic class EntityPacketRewriter1_16_2 extends EntityRewriter<ClientboundPackets1_16_2, Protocol1_16_2To1_16_1> {\n\n    private final Set<String> oldDimensions = Sets.newHashSet(\"minecraft:overworld\", \"minecraft:the_nether\", \"minecraft:the_end\");\n    private boolean warned;\n\n    public EntityPacketRewriter1_16_2(Protocol1_16_2To1_16_1 protocol) {\n        super(protocol);\n    }\n\n    @Override\n    protected void registerPackets() {\n        registerTracker(ClientboundPackets1_16_2.ADD_EXPERIENCE_ORB, EntityTypes1_16_2.EXPERIENCE_ORB);\n        registerTracker(ClientboundPackets1_16_2.ADD_PAINTING, EntityTypes1_16_2.PAINTING);\n        registerTracker(ClientboundPackets1_16_2.ADD_PLAYER, EntityTypes1_16_2.PLAYER);\n        registerSetEntityData(ClientboundPackets1_16_2.SET_ENTITY_DATA, Types1_16.ENTITY_DATA_LIST);\n\n        protocol.registerClientbound(ClientboundPackets1_16_2.LOGIN, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.INT); // Entity ID\n                handler(wrapper -> {\n                    boolean hardcore = wrapper.read(Types.BOOLEAN);\n                    short gamemode = wrapper.read(Types.BYTE);\n                    if (hardcore) {\n                        gamemode |= 0x08;\n                    }\n                    wrapper.write(Types.UNSIGNED_BYTE, gamemode);\n                });\n                map(Types.BYTE); // Previous Gamemode\n                map(Types.STRING_ARRAY); // World List\n                handler(wrapper -> {\n                    CompoundTag registry = wrapper.read(Types.NAMED_COMPOUND_TAG);\n                    if (wrapper.user().getProtocolInfo().protocolVersion().olderThanOrEqualTo(ProtocolVersion.v1_15_2)) {\n                        // Store biomes for <1.16 client handling\n                        ListTag<CompoundTag> biomes = TagUtil.getRegistryEntries(registry, \"worldgen/biome\");\n                        BiomeStorage biomeStorage = wrapper.user().get(BiomeStorage.class);\n                        biomeStorage.clear();\n                        for (CompoundTag biome : biomes) {\n                            StringTag name = biome.getStringTag(\"name\");\n                            NumberTag id = biome.getNumberTag(\"id\");\n                            biomeStorage.addBiome(name.getValue(), id.asInt());\n                        }\n                    } else if (!warned && !ViaBackwards.getConfig().suppressEmulationWarnings()) {\n                        warned = true;\n                        protocol.getLogger().warning(\"1.16 and 1.16.1 clients are only partially supported and may have wrong biomes displayed.\");\n                    }\n\n                    // Just screw the registry and write the defaults for 1.16 and 1.16.1 clients\n                    wrapper.write(Types.NAMED_COMPOUND_TAG, DimensionRegistries1_16.getDimensionsTag());\n\n                    CompoundTag dimensionData = wrapper.read(Types.NAMED_COMPOUND_TAG);\n                    wrapper.write(Types.STRING, getDimensionFromData(dimensionData));\n                });\n                map(Types.STRING); // Dimension\n                handler(wrapper -> {\n                    final String world = wrapper.get(Types.STRING, 1);\n                    trackWorld(wrapper.user(), world);\n                });\n                map(Types.LONG); // Seed\n                handler(wrapper -> {\n                    int maxPlayers = wrapper.read(Types.VAR_INT);\n                    wrapper.write(Types.UNSIGNED_BYTE, (short) Math.min(maxPlayers, 255));\n                });\n                // ...\n                handler(getPlayerTrackerHandler());\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_16_2.RESPAWN, wrapper -> {\n            CompoundTag dimensionData = wrapper.read(Types.NAMED_COMPOUND_TAG);\n            wrapper.write(Types.STRING, getDimensionFromData(dimensionData));\n\n            final String world = wrapper.passthrough(Types.STRING);\n            trackWorld(wrapper.user(), world);\n        });\n    }\n\n    private String getDimensionFromData(CompoundTag dimensionData) {\n        // This may technically break other custom dimension settings for 1.16/1.16.1 clients, so those cases are considered semi \"unsupported\" here\n        StringTag effectsLocation = dimensionData.getStringTag(\"effects\");\n        return effectsLocation != null && oldDimensions.contains(Key.namespaced(effectsLocation.getValue())) ?\n            effectsLocation.getValue() : \"minecraft:overworld\";\n    }\n\n    @Override\n    protected void registerRewrites() {\n        registerEntityDataTypeHandler(Types1_16.ENTITY_DATA_TYPES.itemType, null, Types1_16.ENTITY_DATA_TYPES.optionalBlockStateType,\n            Types1_16.ENTITY_DATA_TYPES.particleType, Types1_16.ENTITY_DATA_TYPES.componentType, Types1_16.ENTITY_DATA_TYPES.optionalComponentType);\n\n        filter().type(EntityTypes1_16_2.ABSTRACT_PIGLIN).index(15).toIndex(16);\n        filter().type(EntityTypes1_16_2.ABSTRACT_PIGLIN).index(16).toIndex(15);\n    }\n\n    @Override\n    public void onMappingDataLoaded() {\n        super.onMappingDataLoaded();\n        mapEntityTypeWithData(EntityTypes1_16_2.PIGLIN_BRUTE, EntityTypes1_16_2.PIGLIN).jsonName();\n    }\n\n    @Override\n    public EntityType typeFromId(int typeId) {\n        return EntityTypes1_16_2.getTypeFromId(typeId);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_16_2to1_16_1/storage/BiomeStorage.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_16_2to1_16_1.storage;\n\nimport com.viaversion.viabackwards.protocol.v1_16_2to1_16_1.data.BiomeMappings1_16_1;\nimport com.viaversion.viaversion.api.connection.StorableObject;\nimport com.viaversion.viaversion.libs.fastutil.ints.Int2IntMap;\nimport com.viaversion.viaversion.libs.fastutil.ints.Int2IntOpenHashMap;\n\npublic final class BiomeStorage implements StorableObject {\n\n    private final Int2IntMap modernToLegacyBiomes = new Int2IntOpenHashMap();\n\n    public BiomeStorage() {\n        modernToLegacyBiomes.defaultReturnValue(-1);\n    }\n\n    public void addBiome(final String biome, final int id) {\n        modernToLegacyBiomes.put(id, BiomeMappings1_16_1.toLegacyBiome(biome));\n    }\n\n    public int legacyBiome(final int biome) {\n        return modernToLegacyBiomes.get(biome);\n    }\n\n    public void clear() {\n        modernToLegacyBiomes.clear();\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_16_3to1_16_2/Protocol1_16_3To1_16_2.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_16_3to1_16_2;\n\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viaversion.protocols.v1_16_1to1_16_2.packet.ClientboundPackets1_16_2;\nimport com.viaversion.viaversion.protocols.v1_16_1to1_16_2.packet.ServerboundPackets1_16_2;\n\npublic class Protocol1_16_3To1_16_2 extends BackwardsProtocol<ClientboundPackets1_16_2, ClientboundPackets1_16_2, ServerboundPackets1_16_2, ServerboundPackets1_16_2> {\n\n    public Protocol1_16_3To1_16_2() {\n        super(ClientboundPackets1_16_2.class, ClientboundPackets1_16_2.class, ServerboundPackets1_16_2.class, ServerboundPackets1_16_2.class);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_16_4to1_16_3/Protocol1_16_4To1_16_3.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_16_4to1_16_3;\n\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viabackwards.protocol.v1_16_4to1_16_3.storage.PlayerHandStorage;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.protocols.v1_16_1to1_16_2.packet.ClientboundPackets1_16_2;\nimport com.viaversion.viaversion.protocols.v1_16_1to1_16_2.packet.ServerboundPackets1_16_2;\n\npublic class Protocol1_16_4To1_16_3 extends BackwardsProtocol<ClientboundPackets1_16_2, ClientboundPackets1_16_2, ServerboundPackets1_16_2, ServerboundPackets1_16_2> {\n\n    public Protocol1_16_4To1_16_3() {\n        super(ClientboundPackets1_16_2.class, ClientboundPackets1_16_2.class, ServerboundPackets1_16_2.class, ServerboundPackets1_16_2.class);\n    }\n\n    @Override\n    protected void registerPackets() {\n        registerServerbound(ServerboundPackets1_16_2.EDIT_BOOK, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.ITEM1_13_2);\n                map(Types.BOOLEAN);\n                handler(wrapper -> {\n                    int slot = wrapper.read(Types.VAR_INT);\n                    if (slot == 1) {\n                        wrapper.write(Types.VAR_INT, 40); // offhand\n                    } else {\n                        wrapper.write(Types.VAR_INT, wrapper.user().get(PlayerHandStorage.class).getCurrentHand());\n                    }\n                });\n            }\n        });\n\n        registerServerbound(ServerboundPackets1_16_2.SET_CARRIED_ITEM, wrapper -> {\n            short slot = wrapper.passthrough(Types.SHORT);\n            wrapper.user().get(PlayerHandStorage.class).setCurrentHand(slot);\n        });\n    }\n\n    @Override\n    public void init(UserConnection user) {\n        user.put(new PlayerHandStorage());\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_16_4to1_16_3/storage/PlayerHandStorage.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_16_4to1_16_3.storage;\n\nimport com.viaversion.viaversion.api.connection.StorableObject;\n\npublic class PlayerHandStorage implements StorableObject {\n\n    private int currentHand;\n\n    public int getCurrentHand() {\n        return currentHand;\n    }\n\n    public void setCurrentHand(int currentHand) {\n        this.currentHand = currentHand;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_16to1_15_2/Protocol1_16To1_15_2.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_16to1_15_2;\n\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viabackwards.protocol.v1_16to1_15_2.data.BackwardsMappingData1_16;\nimport com.viaversion.viabackwards.protocol.v1_16to1_15_2.rewriter.BlockItemPacketRewriter1_16;\nimport com.viaversion.viabackwards.protocol.v1_16to1_15_2.rewriter.CommandRewriter1_16;\nimport com.viaversion.viabackwards.protocol.v1_16to1_15_2.rewriter.EntityPacketRewriter1_16;\nimport com.viaversion.viabackwards.protocol.v1_16to1_15_2.rewriter.TranslatableRewriter1_16;\nimport com.viaversion.viabackwards.protocol.v1_16to1_15_2.storage.PlayerAttributesStorage;\nimport com.viaversion.viabackwards.protocol.v1_16to1_15_2.storage.PlayerSneakStorage;\nimport com.viaversion.viabackwards.protocol.v1_16to1_15_2.storage.WorldNameTracker;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.ClientWorld;\nimport com.viaversion.viaversion.api.minecraft.RegistryType;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_16;\nimport com.viaversion.viaversion.api.protocol.packet.State;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.data.entity.EntityTrackerBase;\nimport com.viaversion.viaversion.libs.gson.JsonElement;\nimport com.viaversion.viaversion.libs.gson.JsonObject;\nimport com.viaversion.viaversion.protocols.base.ClientboundLoginPackets;\nimport com.viaversion.viaversion.protocols.base.ClientboundStatusPackets;\nimport com.viaversion.viaversion.protocols.v1_13_2to1_14.packet.ServerboundPackets1_14;\nimport com.viaversion.viaversion.protocols.v1_14_4to1_15.packet.ClientboundPackets1_15;\nimport com.viaversion.viaversion.protocols.v1_15_2to1_16.packet.ClientboundPackets1_16;\nimport com.viaversion.viaversion.protocols.v1_15_2to1_16.packet.ServerboundPackets1_16;\nimport com.viaversion.viaversion.rewriter.BlockRewriter;\nimport com.viaversion.viaversion.rewriter.ParticleRewriter;\nimport com.viaversion.viaversion.rewriter.TagRewriter;\nimport com.viaversion.viaversion.util.GsonUtil;\nimport java.util.UUID;\n\npublic class Protocol1_16To1_15_2 extends BackwardsProtocol<ClientboundPackets1_16, ClientboundPackets1_15, ServerboundPackets1_16, ServerboundPackets1_14> {\n\n    public static final BackwardsMappingData1_16 MAPPINGS = new BackwardsMappingData1_16();\n    private final EntityPacketRewriter1_16 entityRewriter = new EntityPacketRewriter1_16(this);\n    private final BlockItemPacketRewriter1_16 blockItemPackets = new BlockItemPacketRewriter1_16(this);\n    private final ParticleRewriter<ClientboundPackets1_16> particleRewriter = new ParticleRewriter<>(this);\n    private final TranslatableRewriter1_16 translatableRewriter = new TranslatableRewriter1_16(this);\n    private final TagRewriter<ClientboundPackets1_16> tagRewriter = new TagRewriter<>(this);\n    private final BlockRewriter<ClientboundPackets1_16> blockRewriter = BlockRewriter.for1_14(this);\n\n    public Protocol1_16To1_15_2() {\n        super(ClientboundPackets1_16.class, ClientboundPackets1_15.class, ServerboundPackets1_16.class, ServerboundPackets1_14.class);\n    }\n\n    @Override\n    protected void registerPackets() {\n        super.registerPackets();\n\n        new CommandRewriter1_16(this).registerDeclareCommands(ClientboundPackets1_16.COMMANDS);\n\n        registerClientbound(State.STATUS, ClientboundStatusPackets.STATUS_RESPONSE, wrapper -> {\n            String original = wrapper.passthrough(Types.STRING);\n            JsonObject object = GsonUtil.getGson().fromJson(original, JsonObject.class);\n            JsonElement description = object.get(\"description\");\n            if (description == null) return;\n\n            translatableRewriter.processText(wrapper.user(), description);\n            wrapper.set(Types.STRING, 0, object.toString());\n        });\n\n        replaceClientbound(ClientboundPackets1_16.CHAT, new PacketHandlers() {\n            @Override\n            public void register() {\n                handler(wrapper -> translatableRewriter.processText(wrapper.user(), wrapper.passthrough(Types.COMPONENT)));\n                map(Types.BYTE);\n                read(Types.UUID); // Sender\n            }\n        });\n\n        replaceClientbound(ClientboundPackets1_16.OPEN_SCREEN, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT); // Window Id\n                map(Types.VAR_INT); // Window Type\n                handler(wrapper -> translatableRewriter.processText(wrapper.user(), wrapper.passthrough(Types.COMPONENT)));\n                handler(wrapper -> {\n                    int windowType = wrapper.get(Types.VAR_INT, 1);\n                    if (windowType == 20) { // Smithing table\n                        wrapper.set(Types.VAR_INT, 1, 7); // Open anvil inventory\n                    } else if (windowType > 20) {\n                        wrapper.set(Types.VAR_INT, 1, --windowType);\n                    }\n                });\n            }\n        });\n\n        // Login success\n        registerClientbound(State.LOGIN, ClientboundLoginPackets.LOGIN_FINISHED, wrapper -> {\n            // Transform uuid to plain string\n            UUID uuid = wrapper.read(Types.UUID);\n            wrapper.write(Types.STRING, uuid.toString());\n        });\n\n        tagRewriter.register(ClientboundPackets1_16.UPDATE_TAGS, RegistryType.ENTITY);\n\n        registerServerbound(ServerboundPackets1_14.PLAYER_COMMAND, wrapper -> {\n            wrapper.passthrough(Types.VAR_INT); // player id\n            int action = wrapper.passthrough(Types.VAR_INT);\n            if (action == 0) {\n                wrapper.user().get(PlayerSneakStorage.class).setSneaking(true);\n            } else if (action == 1) {\n                wrapper.user().get(PlayerSneakStorage.class).setSneaking(false);\n            }\n        });\n\n        registerServerbound(ServerboundPackets1_14.INTERACT, wrapper -> {\n            wrapper.passthrough(Types.VAR_INT); // Entity Id\n            int action = wrapper.passthrough(Types.VAR_INT);\n            if (action == 0 || action == 2) {\n                if (action == 2) {\n                    // Location\n                    wrapper.passthrough(Types.FLOAT);\n                    wrapper.passthrough(Types.FLOAT);\n                    wrapper.passthrough(Types.FLOAT);\n                }\n\n                wrapper.passthrough(Types.VAR_INT); // Hand\n            }\n\n            // New boolean: Whether the client is sneaking\n            wrapper.write(Types.BOOLEAN, wrapper.user().get(PlayerSneakStorage.class).isSneaking());\n        });\n\n        registerServerbound(ServerboundPackets1_14.PLAYER_ABILITIES, wrapper -> {\n            byte flags = wrapper.read(Types.BYTE);\n            flags &= 2; // Only take the isFlying value (everything else has been removed and wasn't used anyways)\n            wrapper.write(Types.BYTE, flags);\n\n            wrapper.read(Types.FLOAT);\n            wrapper.read(Types.FLOAT);\n        });\n\n        cancelServerbound(ServerboundPackets1_14.SET_JIGSAW_BLOCK);\n    }\n\n    @Override\n    public void init(UserConnection user) {\n        user.addEntityTracker(this.getClass(), new EntityTrackerBase(user, EntityTypes1_16.PLAYER));\n        user.addClientWorld(this.getClass(), new ClientWorld());\n\n        user.put(new PlayerSneakStorage());\n        user.put(new WorldNameTracker());\n        user.put(new PlayerAttributesStorage());\n    }\n\n    @Override\n    public TranslatableRewriter1_16 getComponentRewriter() {\n        return translatableRewriter;\n    }\n\n    @Override\n    public BackwardsMappingData1_16 getMappingData() {\n        return MAPPINGS;\n    }\n\n    @Override\n    public EntityPacketRewriter1_16 getEntityRewriter() {\n        return entityRewriter;\n    }\n\n    @Override\n    public BlockItemPacketRewriter1_16 getItemRewriter() {\n        return blockItemPackets;\n    }\n\n    @Override\n    public BlockRewriter<ClientboundPackets1_16> getBlockRewriter() {\n        return blockRewriter;\n    }\n\n    @Override\n    public ParticleRewriter<ClientboundPackets1_16> getParticleRewriter() {\n        return particleRewriter;\n    }\n\n    @Override\n    public TagRewriter<ClientboundPackets1_16> getTagRewriter() {\n        return tagRewriter;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_16to1_15_2/data/BackwardsMappingData1_16.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_16to1_15_2.data;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.viabackwards.api.data.BackwardsMappingData;\nimport com.viaversion.viaversion.protocols.v1_15_2to1_16.Protocol1_15_2To1_16;\nimport com.viaversion.viaversion.protocols.v1_15_2to1_16.data.AttributeMappings1_16;\nimport com.viaversion.viaversion.util.Key;\nimport java.util.HashMap;\nimport java.util.Map;\n\npublic class BackwardsMappingData1_16 extends BackwardsMappingData {\n    private final Map<String, String> attributeMappings = new HashMap<>();\n\n    public BackwardsMappingData1_16() {\n        super(\"1.16\", \"1.15\", Protocol1_15_2To1_16.class);\n    }\n\n    @Override\n    protected void loadExtras(final CompoundTag data) {\n        super.loadExtras(data);\n        for (Map.Entry<String, String> entry : AttributeMappings1_16.attributeIdentifierMappings().entrySet()) {\n            attributeMappings.put(Key.stripMinecraftNamespace(entry.getValue()), entry.getKey());\n        }\n    }\n\n    public String mappedAttributeIdentifier(final String identifier) {\n        return attributeMappings.getOrDefault(identifier, identifier);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_16to1_15_2/data/MapColorMappings1_15_2.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_16to1_15_2.data;\n\nimport com.viaversion.viaversion.libs.fastutil.ints.Int2IntMap;\nimport com.viaversion.viaversion.libs.fastutil.ints.Int2IntOpenHashMap;\n\npublic final class MapColorMappings1_15_2 {\n\n    private static final Int2IntMap MAPPINGS = new Int2IntOpenHashMap();\n\n    static {\n        MAPPINGS.put(208, 113);\n        MAPPINGS.put(209, 114);\n        MAPPINGS.put(210, 114);\n        MAPPINGS.put(211, 112);\n        MAPPINGS.put(212, 152);\n        MAPPINGS.put(213, 83);\n        MAPPINGS.put(214, 83);\n        MAPPINGS.put(215, 155);\n        MAPPINGS.put(216, 143);\n        MAPPINGS.put(217, 115);\n        MAPPINGS.put(218, 115);\n        MAPPINGS.put(219, 143);\n        MAPPINGS.put(220, 127);\n        MAPPINGS.put(221, 127);\n        MAPPINGS.put(222, 127);\n        MAPPINGS.put(223, 95);\n        MAPPINGS.put(224, 127);\n        MAPPINGS.put(225, 127);\n        MAPPINGS.put(226, 124);\n        MAPPINGS.put(227, 95);\n        MAPPINGS.put(228, 187);\n        MAPPINGS.put(229, 155);\n        MAPPINGS.put(230, 184);\n        MAPPINGS.put(231, 187);\n        MAPPINGS.put(232, 127);\n        MAPPINGS.put(233, 124);\n        MAPPINGS.put(234, 125);\n        MAPPINGS.put(235, 127);\n    }\n\n    public static int getMappedColor(int color) {\n        return MAPPINGS.getOrDefault(color, -1);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_16to1_15_2/rewriter/BlockItemPacketRewriter1_16.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_16to1_15_2.rewriter;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.IntArrayTag;\nimport com.viaversion.nbt.tag.ListTag;\nimport com.viaversion.nbt.tag.LongArrayTag;\nimport com.viaversion.nbt.tag.StringTag;\nimport com.viaversion.nbt.tag.Tag;\nimport com.viaversion.viabackwards.api.rewriters.BackwardsItemRewriter;\nimport com.viaversion.viabackwards.api.rewriters.EnchantmentRewriter;\nimport com.viaversion.viabackwards.api.rewriters.MapColorRewriter;\nimport com.viaversion.viabackwards.protocol.v1_16_2to1_16_1.storage.BiomeStorage;\nimport com.viaversion.viabackwards.protocol.v1_16to1_15_2.Protocol1_16To1_15_2;\nimport com.viaversion.viabackwards.protocol.v1_16to1_15_2.data.MapColorMappings1_15_2;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.item.Item;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.protocol.version.ProtocolVersion;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.chunk.ChunkType1_15;\nimport com.viaversion.viaversion.api.type.types.chunk.ChunkType1_16;\nimport com.viaversion.viaversion.libs.gson.JsonElement;\nimport com.viaversion.viaversion.protocols.v1_13_2to1_14.packet.ServerboundPackets1_14;\nimport com.viaversion.viaversion.protocols.v1_14_4to1_15.packet.ClientboundPackets1_15;\nimport com.viaversion.viaversion.protocols.v1_15_2to1_16.packet.ClientboundPackets1_16;\nimport com.viaversion.viaversion.protocols.v1_15_2to1_16.rewriter.ItemPacketRewriter1_16;\nimport com.viaversion.viaversion.rewriter.RecipeRewriter;\nimport com.viaversion.viaversion.util.CompactArrayUtil;\nimport com.viaversion.viaversion.util.Key;\nimport com.viaversion.viaversion.util.UUIDUtil;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.UUID;\n\npublic class BlockItemPacketRewriter1_16 extends BackwardsItemRewriter<ClientboundPackets1_16, ServerboundPackets1_14, Protocol1_16To1_15_2> {\n\n    private EnchantmentRewriter enchantmentRewriter;\n\n    public BlockItemPacketRewriter1_16(Protocol1_16To1_15_2 protocol) {\n        super(protocol, Types.ITEM1_13_2, Types.ITEM1_13_2_SHORT_ARRAY);\n    }\n\n    @Override\n    protected void registerPackets() {\n        RecipeRewriter<ClientboundPackets1_16> recipeRewriter = new RecipeRewriter<>(protocol);\n        // Remove new smithing type, only in this handler\n        protocol.registerClientbound(ClientboundPackets1_16.UPDATE_RECIPES, wrapper -> {\n            int size = wrapper.passthrough(Types.VAR_INT);\n            int newSize = size;\n            for (int i = 0; i < size; i++) {\n                String originalType = wrapper.read(Types.STRING);\n                String type = Key.stripMinecraftNamespace(originalType);\n                if (type.equals(\"smithing\")) {\n                    newSize--;\n\n                    wrapper.read(Types.STRING);\n                    wrapper.read(Types.ITEM1_13_2_ARRAY);\n                    wrapper.read(Types.ITEM1_13_2_ARRAY);\n                    wrapper.read(Types.ITEM1_13_2);\n                    continue;\n                }\n\n                wrapper.write(Types.STRING, originalType);\n                wrapper.passthrough(Types.STRING); // Recipe Identifier\n                recipeRewriter.handleRecipeType(wrapper, type);\n            }\n\n            wrapper.set(Types.VAR_INT, 0, newSize);\n        });\n\n        protocol.getBlockRewriter().registerLevelChunk(ClientboundPackets1_16.LEVEL_CHUNK, ChunkType1_16.TYPE, ChunkType1_15.TYPE, (connection, chunk) -> {\n            CompoundTag heightMaps = chunk.getHeightMap();\n            for (Tag heightMapTag : heightMaps.values()) {\n                if (!(heightMapTag instanceof LongArrayTag heightMap)) {\n                    continue;\n                }\n\n                int[] heightMapData = new int[256];\n                CompactArrayUtil.iterateCompactArrayWithPadding(9, heightMapData.length, heightMap.getValue(), (i, v) -> heightMapData[i] = v);\n                heightMap.setValue(CompactArrayUtil.createCompactArray(9, heightMapData.length, i -> heightMapData[i]));\n            }\n\n            if (chunk.isBiomeData()) {\n                if (connection.getProtocolInfo().serverProtocolVersion().newerThanOrEqualTo(ProtocolVersion.v1_16_2)) {\n                    BiomeStorage biomeStorage = connection.get(BiomeStorage.class);\n                    for (int i = 0; i < 1024; i++) {\n                        int biome = chunk.getBiomeData()[i];\n                        int legacyBiome = biomeStorage.legacyBiome(biome);\n                        if (legacyBiome == -1) {\n                            protocol.getLogger().warning(\"Biome sent that does not exist in the biome registry: \" + biome);\n                            legacyBiome = 1;\n                        }\n                        chunk.getBiomeData()[i] = legacyBiome;\n                    }\n                } else {\n                    for (int i = 0; i < 1024; i++) {\n                        int biome = chunk.getBiomeData()[i];\n                        switch (biome) {\n                            case 170, 171, 172, 173 -> chunk.getBiomeData()[i] = 8;\n                        }\n                    }\n                }\n            }\n\n            if (chunk.getBlockEntities() == null) return;\n            for (CompoundTag blockEntity : chunk.getBlockEntities()) {\n                handleBlockEntity(blockEntity);\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_16.SET_EQUIPMENT, ClientboundPackets1_15.SET_EQUIPPED_ITEM, wrapper -> {\n            int entityId = wrapper.passthrough(Types.VAR_INT);\n\n            List<EquipmentData> equipmentData = new ArrayList<>();\n            byte slot;\n            do {\n                slot = wrapper.read(Types.BYTE);\n                Item item = handleItemToClient(wrapper.user(), wrapper.read(Types.ITEM1_13_2));\n                int rawSlot = slot & 0x7F;\n                equipmentData.add(new EquipmentData(rawSlot, item));\n            } while ((slot & 0xFFFFFF80) != 0);\n\n            // Send first data in the current packet\n            EquipmentData firstData = equipmentData.get(0);\n            wrapper.write(Types.VAR_INT, firstData.slot);\n            wrapper.write(Types.ITEM1_13_2, firstData.item);\n\n            // If there are more items, send new packets for them\n            for (int i = 1; i < equipmentData.size(); i++) {\n                PacketWrapper equipmentPacket = wrapper.create(ClientboundPackets1_15.SET_EQUIPPED_ITEM);\n                EquipmentData data = equipmentData.get(i);\n                equipmentPacket.write(Types.VAR_INT, entityId);\n                equipmentPacket.write(Types.VAR_INT, data.slot);\n                equipmentPacket.write(Types.ITEM1_13_2, data.item);\n                equipmentPacket.send(Protocol1_16To1_15_2.class);\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_16.LIGHT_UPDATE, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT); // x\n                map(Types.VAR_INT); // y\n                read(Types.BOOLEAN);\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_16.CONTAINER_SET_DATA, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.UNSIGNED_BYTE); // Window id\n                map(Types.SHORT); // Property\n                map(Types.SHORT); // Value\n                handler(wrapper -> {\n                    short property = wrapper.get(Types.SHORT, 0);\n                    if (property >= 4 && property <= 6) { // Enchantment id\n                        short enchantmentId = wrapper.get(Types.SHORT, 1);\n                        if (enchantmentId > 11) { // soul_speed\n                            wrapper.set(Types.SHORT, 1, --enchantmentId);\n                        } else if (enchantmentId == 11) {\n                            wrapper.set(Types.SHORT, 1, (short) 9);\n                        }\n                    }\n                });\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_16.MAP_ITEM_DATA, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT); // Map ID\n                map(Types.BYTE); // Scale\n                map(Types.BOOLEAN); // Tracking Position\n                map(Types.BOOLEAN); // Locked\n                handler(MapColorRewriter.getRewriteHandler(MapColorMappings1_15_2::getMappedColor));\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_16.BLOCK_ENTITY_DATA, wrapper -> {\n            wrapper.passthrough(Types.BLOCK_POSITION1_14); // Position\n            wrapper.passthrough(Types.UNSIGNED_BYTE); // Action\n            CompoundTag tag = wrapper.passthrough(Types.NAMED_COMPOUND_TAG);\n            handleBlockEntity(tag);\n        });\n\n        protocol.registerServerbound(ServerboundPackets1_14.EDIT_BOOK, wrapper -> handleItemToServer(wrapper.user(), wrapper.passthrough(Types.ITEM1_13_2)));\n    }\n\n    private void handleBlockEntity(CompoundTag tag) {\n        String id = tag.getString(\"id\");\n        if (id == null) return;\n\n        id = Key.namespaced(id);\n        if (id.equals(\"minecraft:conduit\")) {\n            Tag targetUuidTag = tag.remove(\"Target\");\n            if (!(targetUuidTag instanceof IntArrayTag)) return;\n\n            // Target -> target_uuid\n            UUID targetUuid = UUIDUtil.fromIntArray((int[]) targetUuidTag.getValue());\n            tag.putString(\"target_uuid\", targetUuid.toString());\n        } else if (id.equals(\"minecraft:skull\")) {\n            if (!(tag.remove(\"SkullOwner\") instanceof CompoundTag skullOwnerTag)) return;\n\n            if (skullOwnerTag.remove(\"Id\") instanceof IntArrayTag ownerUuidTag) {\n                UUID ownerUuid = UUIDUtil.fromIntArray(ownerUuidTag.getValue());\n                skullOwnerTag.putString(\"Id\", ownerUuid.toString());\n            }\n\n            // SkullOwner -> Owner\n            CompoundTag ownerTag = new CompoundTag();\n            for (Map.Entry<String, Tag> entry : skullOwnerTag) {\n                ownerTag.put(entry.getKey(), entry.getValue());\n            }\n            tag.put(\"Owner\", ownerTag);\n        }\n    }\n\n    @Override\n    protected void registerRewrites() {\n        enchantmentRewriter = new EnchantmentRewriter(this);\n        enchantmentRewriter.registerEnchantment(\"minecraft:soul_speed\", \"§7Soul Speed\");\n    }\n\n    @Override\n    public Item handleItemToClient(UserConnection connection, Item item) {\n        if (item == null) return null;\n\n        item = super.handleItemToClient(connection, item);\n\n        CompoundTag tag = item.tag();\n        if (item.identifier() == 771 && tag != null) {\n            CompoundTag ownerTag = tag.getCompoundTag(\"SkullOwner\");\n            if (ownerTag != null) {\n                IntArrayTag idTag = ownerTag.getIntArrayTag(\"Id\");\n                if (idTag != null) {\n                    UUID ownerUuid = UUIDUtil.fromIntArray(idTag.getValue());\n                    ownerTag.putString(\"Id\", ownerUuid.toString());\n                }\n            }\n        }\n\n        // Handle hover event changes in written book pages\n        if (item.identifier() == 759 && tag != null) {\n            ListTag<StringTag> pagesTag = tag.getListTag(\"pages\", StringTag.class);\n            if (pagesTag != null) {\n                for (StringTag page : pagesTag) {\n                    JsonElement jsonElement = protocol.getComponentRewriter().processText(connection, page.getValue());\n                    page.setValue(jsonElement.toString());\n                }\n            }\n        }\n\n        ItemPacketRewriter1_16.newToOldAttributes(item);\n        enchantmentRewriter.handleToClient(item);\n        return item;\n    }\n\n    @Override\n    public Item handleItemToServer(UserConnection connection, Item item) {\n        if (item == null) return null;\n\n        int identifier = item.identifier();\n        item = super.handleItemToServer(connection, item);\n\n        CompoundTag tag = item.tag();\n        if (identifier == 771 && tag != null) {\n            CompoundTag ownerTag = tag.getCompoundTag(\"SkullOwner\");\n            if (ownerTag != null) {\n                StringTag idTag = ownerTag.getStringTag(\"Id\");\n                if (idTag != null) {\n                    UUID ownerUuid = UUID.fromString(idTag.getValue());\n                    ownerTag.put(\"Id\", new IntArrayTag(UUIDUtil.toIntArray(ownerUuid)));\n                }\n            }\n        }\n\n        ItemPacketRewriter1_16.oldToNewAttributes(item);\n        enchantmentRewriter.handleToServer(item);\n        return item;\n    }\n\n    private record EquipmentData(int slot, Item item) {\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_16to1_15_2/rewriter/CommandRewriter1_16.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_16to1_15_2.rewriter;\n\nimport com.viaversion.viabackwards.protocol.v1_16to1_15_2.Protocol1_16To1_15_2;\nimport com.viaversion.viaversion.protocols.v1_15_2to1_16.packet.ClientboundPackets1_16;\nimport com.viaversion.viaversion.rewriter.CommandRewriter;\nimport org.checkerframework.checker.nullness.qual.Nullable;\n\npublic class CommandRewriter1_16 extends CommandRewriter<ClientboundPackets1_16> {\n\n    public CommandRewriter1_16(Protocol1_16To1_15_2 protocol) {\n        super(protocol);\n    }\n\n    @Override\n    public @Nullable String handleArgumentType(String argumentType) {\n        if (argumentType.equals(\"minecraft:uuid\")) {\n            return \"minecraft:game_profile\";\n        }\n        return super.handleArgumentType(argumentType);\n    }\n\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_16to1_15_2/rewriter/EntityPacketRewriter1_16.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_16to1_15_2.rewriter;\n\nimport com.viaversion.viabackwards.api.rewriters.EntityRewriter;\nimport com.viaversion.viabackwards.protocol.v1_16to1_15_2.Protocol1_16To1_15_2;\nimport com.viaversion.viabackwards.protocol.v1_16to1_15_2.storage.PlayerAttributesStorage;\nimport com.viaversion.viabackwards.protocol.v1_16to1_15_2.storage.WolfDataMaskStorage;\nimport com.viaversion.viabackwards.protocol.v1_16to1_15_2.storage.WorldNameTracker;\nimport com.viaversion.viaversion.api.Via;\nimport com.viaversion.viaversion.api.data.entity.StoredEntityData;\nimport com.viaversion.viaversion.api.minecraft.ClientWorld;\nimport com.viaversion.viaversion.api.minecraft.Particle;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityType;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_16;\nimport com.viaversion.viaversion.api.minecraft.entitydata.EntityData;\nimport com.viaversion.viaversion.api.minecraft.entitydata.EntityDataType;\nimport com.viaversion.viaversion.api.minecraft.item.Item;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.protocol.remapper.ValueTransformer;\nimport com.viaversion.viaversion.api.protocol.version.ProtocolVersion;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.version.Types1_14;\nimport com.viaversion.viaversion.api.type.types.version.Types1_16;\nimport com.viaversion.viaversion.libs.gson.JsonElement;\nimport com.viaversion.viaversion.protocols.v1_14_4to1_15.packet.ClientboundPackets1_15;\nimport com.viaversion.viaversion.protocols.v1_15_2to1_16.packet.ClientboundPackets1_16;\nimport com.viaversion.viaversion.util.Key;\nimport java.util.UUID;\n\npublic class EntityPacketRewriter1_16 extends EntityRewriter<ClientboundPackets1_16, Protocol1_16To1_15_2> {\n\n    private final ValueTransformer<String, Integer> dimensionTransformer = new ValueTransformer<>(Types.STRING, Types.INT) {\n        @Override\n        public Integer transform(PacketWrapper wrapper, String input) {\n            input = Key.namespaced(input);\n            return switch (input) {\n                case \"minecraft:the_nether\" -> -1;\n                case \"minecraft:the_end\" -> 1;\n                default -> 0; // Including overworld\n            };\n        }\n    };\n\n    public EntityPacketRewriter1_16(Protocol1_16To1_15_2 protocol) {\n        super(protocol);\n    }\n\n    @Override\n    protected void registerPackets() {\n        protocol.replaceClientbound(ClientboundPackets1_16.ADD_ENTITY, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT); // 0 - Entity id\n                map(Types.UUID); // 1 - Entity UUID\n                map(Types.VAR_INT); // 2 - Entity Type\n                map(Types.DOUBLE); // 3 - X\n                map(Types.DOUBLE); // 4 - Y\n                map(Types.DOUBLE); // 5 - Z\n                map(Types.BYTE); // 6 - Pitch\n                map(Types.BYTE); // 7 - Yaw\n                map(Types.INT); // 8 - Data\n                handler(wrapper -> {\n                    EntityType entityType = typeFromId(wrapper.get(Types.VAR_INT, 1));\n                    if (entityType == EntityTypes1_16.LIGHTNING_BOLT) {\n                        // Map to old weather entity packet\n                        wrapper.cancel();\n\n                        PacketWrapper spawnLightningPacket = wrapper.create(ClientboundPackets1_15.ADD_GLOBAL_ENTITY);\n                        spawnLightningPacket.write(Types.VAR_INT, wrapper.get(Types.VAR_INT, 0)); // Entity id\n                        spawnLightningPacket.write(Types.BYTE, (byte) 1); // Lightning type\n                        spawnLightningPacket.write(Types.DOUBLE, wrapper.get(Types.DOUBLE, 0)); // X\n                        spawnLightningPacket.write(Types.DOUBLE, wrapper.get(Types.DOUBLE, 1)); // Y\n                        spawnLightningPacket.write(Types.DOUBLE, wrapper.get(Types.DOUBLE, 2)); // Z\n                        spawnLightningPacket.send(Protocol1_16To1_15_2.class);\n                    }\n                });\n                handler(getSpawnTrackerWithDataHandler());\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_16.RESPAWN, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(dimensionTransformer); // Dimension Type\n                handler(wrapper -> {\n                    // Grab the tracker for world names\n                    WorldNameTracker worldNameTracker = wrapper.user().get(WorldNameTracker.class);\n                    String nextWorldName = wrapper.read(Types.STRING); // World Name\n\n                    wrapper.passthrough(Types.LONG); // Seed\n                    wrapper.passthrough(Types.UNSIGNED_BYTE); // Gamemode\n                    wrapper.read(Types.BYTE); // Previous gamemode\n\n                    // Grab client world\n                    ClientWorld clientWorld = wrapper.user().getClientWorld(Protocol1_16To1_15_2.class);\n                    int dimension = wrapper.get(Types.INT, 0);\n\n                    // Send a dummy respawn with a different dimension if the world name was different and the same dimension was used\n                    if (clientWorld.getEnvironment() != null && dimension == clientWorld.getEnvironment().id()\n                        && (wrapper.user().isClientSide() || Via.getPlatform().isProxy()\n                        || wrapper.user().getProtocolInfo().protocolVersion().olderThanOrEqualTo(ProtocolVersion.v1_12_2) // Hotfix for https://github.com/ViaVersion/ViaBackwards/issues/381\n                        || !nextWorldName.equals(worldNameTracker.getWorldName()))) {\n                        PacketWrapper packet = wrapper.create(ClientboundPackets1_15.RESPAWN);\n                        packet.write(Types.INT, dimension == 0 ? -1 : 0);\n                        packet.write(Types.LONG, 0L);\n                        packet.write(Types.UNSIGNED_BYTE, (short) 0);\n                        packet.write(Types.STRING, \"default\");\n                        packet.send(Protocol1_16To1_15_2.class);\n                    }\n\n                    if (clientWorld.setEnvironment(dimension)) {\n                        tracker(wrapper.user()).clearEntities();\n                    }\n\n                    wrapper.write(Types.STRING, \"default\"); // Level type\n                    wrapper.read(Types.BOOLEAN); // Debug\n                    if (wrapper.read(Types.BOOLEAN)) {\n                        wrapper.set(Types.STRING, 0, \"flat\");\n                    }\n\n                    final PlayerAttributesStorage attributes = wrapper.user().get(PlayerAttributesStorage.class);\n                    final boolean keepPlayerAttributes = wrapper.read(Types.BOOLEAN);\n                    if (keepPlayerAttributes) {\n                        // Ensure packet order\n                        wrapper.send(Protocol1_16To1_15_2.class);\n                        wrapper.cancel();\n                        attributes.sendAttributes(wrapper.user(), tracker(wrapper.user()).clientEntityId());\n                    } else {\n                        attributes.clearAttributes();\n                    }\n\n                    // Finally update the world name\n                    worldNameTracker.setWorldName(nextWorldName);\n                });\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_16.LOGIN, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.INT); //  Entity ID\n                map(Types.UNSIGNED_BYTE); // Gamemode\n                read(Types.BYTE); // Previous gamemode\n                read(Types.STRING_ARRAY); // World list\n                read(Types.NAMED_COMPOUND_TAG); // whatever this is\n                map(dimensionTransformer); // Dimension Type\n                handler(wrapper -> {\n                    WorldNameTracker worldNameTracker = wrapper.user().get(WorldNameTracker.class);\n                    worldNameTracker.setWorldName(wrapper.read(Types.STRING)); // Save the world name\n                });\n                map(Types.LONG); // Seed\n                map(Types.UNSIGNED_BYTE); // Max players\n                handler(wrapper -> {\n                    ClientWorld clientWorld = wrapper.user().getClientWorld(Protocol1_16To1_15_2.class);\n                    clientWorld.setEnvironment(wrapper.get(Types.INT, 1));\n\n                    wrapper.write(Types.STRING, \"default\"); // Level type\n\n                    wrapper.passthrough(Types.VAR_INT); // View distance\n                    wrapper.passthrough(Types.BOOLEAN); // Reduced debug info\n                    wrapper.passthrough(Types.BOOLEAN); // Show death screen\n\n                    wrapper.read(Types.BOOLEAN); // Debug\n                    if (wrapper.read(Types.BOOLEAN)) {\n                        wrapper.set(Types.STRING, 0, \"flat\");\n                    }\n                });\n                handler(playerTrackerHandler());\n            }\n        });\n\n        registerTracker(ClientboundPackets1_16.ADD_EXPERIENCE_ORB, EntityTypes1_16.EXPERIENCE_ORB);\n        // F Spawn Global Object, it is no longer with us :(\n        registerTracker(ClientboundPackets1_16.ADD_PAINTING, EntityTypes1_16.PAINTING);\n        registerTracker(ClientboundPackets1_16.ADD_PLAYER, EntityTypes1_16.PLAYER);\n        registerSetEntityData(ClientboundPackets1_16.SET_ENTITY_DATA, Types1_16.ENTITY_DATA_LIST, Types1_14.ENTITY_DATA_LIST);\n\n        protocol.registerClientbound(ClientboundPackets1_16.UPDATE_ATTRIBUTES, wrapper -> {\n            final PlayerAttributesStorage attributes = wrapper.user().get(PlayerAttributesStorage.class);\n\n            final int entityId = wrapper.passthrough(Types.VAR_INT);\n\n            final int size = wrapper.passthrough(Types.INT);\n            for (int i = 0; i < size; i++) {\n                final String identifier = Key.stripMinecraftNamespace(wrapper.read(Types.STRING));\n\n                final String mappedIdentifier = protocol.getMappingData().mappedAttributeIdentifier(identifier);\n                wrapper.write(Types.STRING, mappedIdentifier);\n                final double value = wrapper.passthrough(Types.DOUBLE);\n\n                final int count = wrapper.passthrough(Types.VAR_INT);\n                final var modifiers = new PlayerAttributesStorage.AttributeModifier[count];\n                for (int j = 0; j < count; j++) {\n                    final UUID uuid = wrapper.passthrough(Types.UUID);\n                    final double amount = wrapper.passthrough(Types.DOUBLE);\n                    final byte operation = wrapper.passthrough(Types.BYTE);\n\n                    modifiers[j] = new PlayerAttributesStorage.AttributeModifier(uuid, amount, operation);\n                }\n                if (entityId == tracker(wrapper.user()).clientEntityId()) {\n                    attributes.addAttribute(mappedIdentifier, new PlayerAttributesStorage.Attribute(value, modifiers));\n                }\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_16.PLAYER_INFO, wrapper -> {\n            int action = wrapper.passthrough(Types.VAR_INT);\n            int playerCount = wrapper.passthrough(Types.VAR_INT);\n            for (int i = 0; i < playerCount; i++) {\n                wrapper.passthrough(Types.UUID);\n                if (action == 0) { // Add\n                    wrapper.passthrough(Types.STRING);\n                    wrapper.passthrough(Types.PROFILE_PROPERTY_ARRAY);\n                    wrapper.passthrough(Types.VAR_INT);\n                    wrapper.passthrough(Types.VAR_INT);\n                    // Display Name\n                    protocol.getComponentRewriter().processText(wrapper.user(), wrapper.passthrough(Types.OPTIONAL_COMPONENT));\n                } else if (action == 1) { // Update Game Mode\n                    wrapper.passthrough(Types.VAR_INT);\n                } else if (action == 2) { // Update Ping\n                    wrapper.passthrough(Types.VAR_INT);\n                } else if (action == 3) { // Update Display Name\n                    // Display name\n                    protocol.getComponentRewriter().processText(wrapper.user(), wrapper.passthrough(Types.OPTIONAL_COMPONENT));\n                } // 4 = Remove Player\n            }\n        });\n    }\n\n    @Override\n    protected void registerRewrites() {\n        filter().handler((event, data) -> {\n            data.setDataType(Types1_14.ENTITY_DATA_TYPES.byId(data.dataType().typeId()));\n\n            EntityDataType type = data.dataType();\n            if (type == Types1_14.ENTITY_DATA_TYPES.itemType) {\n                data.setValue(protocol.getItemRewriter().handleItemToClient(event.user(), (Item) data.getValue()));\n            } else if (type == Types1_14.ENTITY_DATA_TYPES.optionalBlockStateType) {\n                data.setValue(protocol.getMappingData().getNewBlockStateId((int) data.getValue()));\n            } else if (type == Types1_14.ENTITY_DATA_TYPES.particleType) {\n                protocol.getParticleRewriter().rewriteParticle(event.user(), (Particle) data.getValue());\n            } else if (type == Types1_14.ENTITY_DATA_TYPES.optionalComponentType) {\n                JsonElement text = data.value();\n                if (text != null) {\n                    protocol.getComponentRewriter().processText(event.user(), text);\n                }\n            }\n        });\n\n        filter().type(EntityTypes1_16.ZOGLIN).cancel(16);\n        filter().type(EntityTypes1_16.HOGLIN).cancel(15);\n\n        filter().type(EntityTypes1_16.PIGLIN).cancel(16);\n        filter().type(EntityTypes1_16.PIGLIN).cancel(17);\n        filter().type(EntityTypes1_16.PIGLIN).cancel(18);\n\n        filter().type(EntityTypes1_16.STRIDER).index(15).handler((event, data) -> {\n            boolean baby = data.value();\n            data.setTypeAndValue(Types1_14.ENTITY_DATA_TYPES.varIntType, baby ? 1 : 3);\n        });\n        filter().type(EntityTypes1_16.STRIDER).cancel(16);\n        filter().type(EntityTypes1_16.STRIDER).cancel(17);\n        filter().type(EntityTypes1_16.STRIDER).cancel(18);\n\n        filter().type(EntityTypes1_16.FISHING_BOBBER).cancel(8);\n\n        filter().type(EntityTypes1_16.ABSTRACT_ARROW).cancel(8);\n        filter().type(EntityTypes1_16.ABSTRACT_ARROW).handler((event, data) -> {\n            if (event.index() >= 8) {\n                event.setIndex(event.index() + 1);\n            }\n        });\n\n        filter().type(EntityTypes1_16.WOLF).index(16).handler((event, data) -> {\n            byte mask = data.value();\n            StoredEntityData entityData = tracker(event.user()).entityData(event.entityId());\n            entityData.put(new WolfDataMaskStorage(mask));\n        });\n\n        filter().type(EntityTypes1_16.WOLF).index(20).handler((event, data) -> {\n            StoredEntityData entityData = tracker(event.user()).entityDataIfPresent(event.entityId());\n            byte previousMask = 0;\n            if (entityData != null) {\n                WolfDataMaskStorage wolfData = entityData.get(WolfDataMaskStorage.class);\n                if (wolfData != null) {\n                    previousMask = wolfData.tameableMask();\n                }\n            }\n\n            int angerTime = data.value();\n            byte tameableMask = (byte) (angerTime > 0 ? previousMask | 2 : previousMask & -3);\n            event.createExtraData(new EntityData(16, Types1_14.ENTITY_DATA_TYPES.byteType, tameableMask));\n            event.cancel();\n        });\n    }\n\n    @Override\n    public void onMappingDataLoaded() {\n        super.onMappingDataLoaded();\n        mapEntityTypeWithData(EntityTypes1_16.HOGLIN, EntityTypes1_16.COW).jsonName();\n        mapEntityTypeWithData(EntityTypes1_16.ZOGLIN, EntityTypes1_16.COW).jsonName();\n        mapEntityTypeWithData(EntityTypes1_16.PIGLIN, EntityTypes1_16.ZOMBIFIED_PIGLIN).jsonName();\n        mapEntityTypeWithData(EntityTypes1_16.STRIDER, EntityTypes1_16.MAGMA_CUBE).jsonName();\n    }\n\n    @Override\n    public EntityType typeFromId(int typeId) {\n        return EntityTypes1_16.getTypeFromId(typeId);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_16to1_15_2/rewriter/TranslatableRewriter1_16.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_16to1_15_2.rewriter;\n\nimport com.viaversion.viabackwards.api.rewriters.text.JsonNBTComponentRewriter;\nimport com.viaversion.viabackwards.protocol.v1_16to1_15_2.Protocol1_16To1_15_2;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.libs.gson.JsonElement;\nimport com.viaversion.viaversion.libs.gson.JsonObject;\nimport com.viaversion.viaversion.libs.gson.JsonPrimitive;\nimport com.viaversion.viaversion.protocols.v1_15_2to1_16.packet.ClientboundPackets1_16;\nimport com.viaversion.viaversion.util.ComponentUtil;\nimport com.viaversion.viaversion.util.SerializerVersion;\n\npublic class TranslatableRewriter1_16 extends JsonNBTComponentRewriter<ClientboundPackets1_16> {\n\n    private static final ChatColor[] COLORS = {\n        new ChatColor(\"black\", 0x000000),\n        new ChatColor(\"dark_blue\", 0x0000aa),\n        new ChatColor(\"dark_green\", 0x00aa00),\n        new ChatColor(\"dark_aqua\", 0x00aaaa),\n        new ChatColor(\"dark_red\", 0xaa0000),\n        new ChatColor(\"dark_purple\", 0xaa00aa),\n        new ChatColor(\"gold\", 0xffaa00),\n        new ChatColor(\"gray\", 0xaaaaaa),\n        new ChatColor(\"dark_gray\", 0x555555),\n        new ChatColor(\"blue\", 0x5555ff),\n        new ChatColor(\"green\", 0x55ff55),\n        new ChatColor(\"aqua\", 0x55ffff),\n        new ChatColor(\"red\", 0xff5555),\n        new ChatColor(\"light_purple\", 0xff55ff),\n        new ChatColor(\"yellow\", 0xffff55),\n        new ChatColor(\"white\", 0xffffff)\n    };\n\n    public TranslatableRewriter1_16(Protocol1_16To1_15_2 protocol) {\n        super(protocol, ReadType.JSON);\n    }\n\n    @Override\n    public void processText(UserConnection connection, JsonElement value) {\n        super.processText(connection, value);\n\n        if (value == null || !value.isJsonObject()) return;\n\n        // c o l o r s\n        JsonObject object = value.getAsJsonObject();\n        JsonPrimitive color = object.getAsJsonPrimitive(\"color\");\n        if (color != null) {\n            String colorName = color.getAsString();\n            if (!colorName.isEmpty() && colorName.charAt(0) == '#') {\n                int rgb = Integer.parseInt(colorName.substring(1), 16);\n                String closestChatColor = getClosestChatColor(rgb);\n                object.addProperty(\"color\", closestChatColor);\n            }\n        }\n\n        JsonObject clickEvent = object.getAsJsonObject(\"clickEvent\");\n        if (clickEvent != null && clickEvent.has(\"action\")) {\n            String action = clickEvent.get(\"action\").getAsString();\n            if (action.equals(\"copy_to_clipboard\")) {\n                clickEvent.addProperty(\"action\", \"suggest_command\");\n            }\n        }\n\n        JsonObject hoverEvent = object.getAsJsonObject(\"hoverEvent\");\n        if (hoverEvent == null || !hoverEvent.has(\"contents\")) {\n            return;\n        }\n\n        // show_text as chat component json, show_entity and show_item serialized as snbt\n        JsonObject convertedObject = (JsonObject) ComponentUtil.convertJson(object, SerializerVersion.V1_16, SerializerVersion.V1_15);\n        object.add(\"hoverEvent\", convertedObject.getAsJsonObject(\"hoverEvent\"));\n    }\n\n    private String getClosestChatColor(int rgb) {\n        int r = (rgb >> 16) & 0xFF;\n        int g = (rgb >> 8) & 0xFF;\n        int b = rgb & 0xFF;\n\n        ChatColor closest = null;\n        int smallestDiff = 0;\n\n        for (ChatColor color : COLORS) {\n            if (color.rgb == rgb) {\n                return color.colorName;\n            }\n\n            // Check by the greatest diff of the 3 values\n            int rAverage = (color.r + r) / 2;\n            int rDiff = color.r - r;\n            int gDiff = color.g - g;\n            int bDiff = color.b - b;\n            int diff = ((2 + (rAverage >> 8)) * rDiff * rDiff)\n                + (4 * gDiff * gDiff)\n                + ((2 + ((255 - rAverage) >> 8)) * bDiff * bDiff);\n            if (closest == null || diff < smallestDiff) {\n                closest = color;\n                smallestDiff = diff;\n            }\n        }\n        return closest.colorName;\n    }\n\n    private static final class ChatColor {\n\n        private final String colorName;\n        private final int rgb;\n        private final int r;\n        private final int g;\n        private final int b;\n\n        ChatColor(String colorName, int rgb) {\n            this.colorName = colorName;\n            this.rgb = rgb;\n            r = (rgb >> 16) & 0xFF;\n            g = (rgb >> 8) & 0xFF;\n            b = rgb & 0xFF;\n        }\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_16to1_15_2/storage/PlayerAttributesStorage.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_16to1_15_2.storage;\n\nimport com.viaversion.viabackwards.protocol.v1_16to1_15_2.Protocol1_16To1_15_2;\nimport com.viaversion.viaversion.api.connection.StorableObject;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.protocols.v1_14_4to1_15.packet.ClientboundPackets1_15;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.UUID;\n\npublic final class PlayerAttributesStorage implements StorableObject {\n\n    private final Map<String, Attribute> attributes = new HashMap<>();\n\n    public void sendAttributes(final UserConnection connection, final int entityId) {\n        final PacketWrapper updateAttributes = PacketWrapper.create(ClientboundPackets1_15.UPDATE_ATTRIBUTES, connection);\n\n        updateAttributes.write(Types.VAR_INT, entityId);\n        updateAttributes.write(Types.INT, attributes.size());\n        for (final Map.Entry<String, Attribute> attributeEntry : attributes.entrySet()) {\n            final Attribute attribute = attributeEntry.getValue();\n            updateAttributes.write(Types.STRING, attributeEntry.getKey());\n            updateAttributes.write(Types.DOUBLE, attribute.value());\n            updateAttributes.write(Types.VAR_INT, attribute.modifiers().length);\n\n            for (final AttributeModifier modifier : attribute.modifiers()) {\n                updateAttributes.write(Types.UUID, modifier.uuid());\n                updateAttributes.write(Types.DOUBLE, modifier.amount());\n                updateAttributes.write(Types.BYTE, modifier.operation());\n            }\n        }\n        updateAttributes.send(Protocol1_16To1_15_2.class);\n    }\n\n    public void clearAttributes() {\n        attributes.clear();\n    }\n\n    public void addAttribute(final String key, final Attribute attribute) {\n        attributes.put(key, attribute);\n    }\n\n    public record Attribute(double value, AttributeModifier[] modifiers) {\n    }\n\n    public record AttributeModifier(UUID uuid, double amount, byte operation) {\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_16to1_15_2/storage/PlayerSneakStorage.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_16to1_15_2.storage;\n\nimport com.viaversion.viaversion.api.connection.StorableObject;\n\npublic class PlayerSneakStorage implements StorableObject {\n    private boolean sneaking;\n\n    public boolean isSneaking() {\n        return sneaking;\n    }\n\n    public void setSneaking(boolean sneaking) {\n        this.sneaking = sneaking;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_16to1_15_2/storage/WolfDataMaskStorage.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_16to1_15_2.storage;\n\npublic final class WolfDataMaskStorage {\n\n    private byte tameableMask;\n\n    public WolfDataMaskStorage(byte tameableMask) {\n        this.tameableMask = tameableMask;\n    }\n\n    public void setTameableMask(byte tameableMask) {\n        this.tameableMask = tameableMask;\n    }\n\n    public byte tameableMask() {\n        return tameableMask;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_16to1_15_2/storage/WorldNameTracker.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_16to1_15_2.storage;\n\nimport com.viaversion.viaversion.api.connection.StorableObject;\n\npublic class WorldNameTracker implements StorableObject {\n    private String worldName;\n\n    public String getWorldName() {\n        return worldName;\n    }\n\n    public void setWorldName(String worldName) {\n        this.worldName = worldName;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_17_1to1_17/Protocol1_17_1To1_17.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_17_1to1_17;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.ListTag;\nimport com.viaversion.nbt.tag.StringTag;\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viabackwards.protocol.v1_17_1to1_17.storage.InventoryStateIds;\nimport com.viaversion.viabackwards.protocol.v1_17to1_16_4.storage.PlayerLastCursorItem;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.item.Item;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.protocols.v1_16_4to1_17.packet.ClientboundPackets1_17;\nimport com.viaversion.viaversion.protocols.v1_16_4to1_17.packet.ServerboundPackets1_17;\nimport com.viaversion.viaversion.protocols.v1_17to1_17_1.packet.ClientboundPackets1_17_1;\n\npublic final class Protocol1_17_1To1_17 extends BackwardsProtocol<ClientboundPackets1_17_1, ClientboundPackets1_17, ServerboundPackets1_17, ServerboundPackets1_17> {\n\n    private static final int MAX_PAGE_LENGTH = 8192;\n    private static final int MAX_TITLE_LENGTH = 128;\n    private static final int MAX_PAGES = 200; // Actually limited to 100 when handling the read packet, but /shrug\n\n    public Protocol1_17_1To1_17() {\n        super(ClientboundPackets1_17_1.class, ClientboundPackets1_17.class, ServerboundPackets1_17.class, ServerboundPackets1_17.class);\n    }\n\n    @Override\n    protected void registerPackets() {\n        registerClientbound(ClientboundPackets1_17_1.REMOVE_ENTITIES, null, wrapper -> {\n            int[] entityIds = wrapper.read(Types.VAR_INT_ARRAY_PRIMITIVE);\n            wrapper.cancel();\n            for (int entityId : entityIds) {\n                // Send individual remove packets\n                PacketWrapper newPacket = wrapper.create(ClientboundPackets1_17.REMOVE_ENTITY);\n                newPacket.write(Types.VAR_INT, entityId);\n                newPacket.send(Protocol1_17_1To1_17.class);\n            }\n        });\n\n        registerClientbound(ClientboundPackets1_17_1.CONTAINER_CLOSE, wrapper -> {\n            short containerId = wrapper.passthrough(Types.UNSIGNED_BYTE);\n            wrapper.user().get(InventoryStateIds.class).removeStateId(containerId);\n        });\n        registerClientbound(ClientboundPackets1_17_1.CONTAINER_SET_SLOT, wrapper -> {\n            byte containerId = wrapper.passthrough(Types.BYTE);\n            int stateId = wrapper.read(Types.VAR_INT);\n            wrapper.user().get(InventoryStateIds.class).setStateId(containerId, stateId);\n        });\n        registerClientbound(ClientboundPackets1_17_1.CONTAINER_SET_CONTENT, wrapper -> {\n            short containerId = wrapper.passthrough(Types.UNSIGNED_BYTE);\n            int stateId = wrapper.read(Types.VAR_INT);\n            wrapper.user().get(InventoryStateIds.class).setStateId(containerId, stateId);\n\n            // Length is encoded as a var int in 1.17.1\n            wrapper.write(Types.ITEM1_13_2_SHORT_ARRAY, wrapper.read(Types.ITEM1_13_2_ARRAY));\n\n            // Carried item - forward as CONTAINER_SET_SLOT for <1.17 clients\n            Item carried = wrapper.read(Types.ITEM1_13_2);\n\n            PlayerLastCursorItem lastCursorItem = wrapper.user().get(PlayerLastCursorItem.class);\n            if (lastCursorItem != null) {\n                // For click drag ghost item fix -- since the state ID is always wrong,\n                // the server always resends the entire window contents after a drag action,\n                // which is useful since we need to update the carried item in preparation\n                // for a subsequent drag\n\n                lastCursorItem.setLastCursorItem(carried);\n\n                // In 1.17.1+, the carried/cursor item is part of CONTAINER_SET_CONTENT,\n                // but <1.17 clients don't have this field. Send it as a separate\n                // CONTAINER_SET_SLOT packet so the client's cursor state is properly synced.\n                // This fixes inventory desyncs when the server cancels InventoryClickEvents.\n                PacketWrapper cursorPacket = wrapper.create(ClientboundPackets1_17.CONTAINER_SET_SLOT);\n                cursorPacket.write(Types.BYTE, (byte) -1); // Window ID: -1 for cursor\n                cursorPacket.write(Types.SHORT, (short) -1); // Slot: -1 for cursor\n                cursorPacket.write(Types.ITEM1_13_2, carried);\n                cursorPacket.send(Protocol1_17_1To1_17.class);\n            }\n        });\n\n        registerServerbound(ServerboundPackets1_17.CONTAINER_CLOSE, wrapper -> {\n            byte containerId = wrapper.passthrough(Types.BYTE);\n            wrapper.user().get(InventoryStateIds.class).removeStateId(containerId);\n        });\n        registerServerbound(ServerboundPackets1_17.CONTAINER_CLICK, wrapper -> {\n            byte containerId = wrapper.passthrough(Types.BYTE);\n            int stateId = wrapper.user().get(InventoryStateIds.class).removeStateId(containerId);\n            wrapper.write(Types.VAR_INT, stateId == Integer.MAX_VALUE ? 0 : stateId);\n        });\n\n        registerServerbound(ServerboundPackets1_17.EDIT_BOOK, wrapper -> {\n            Item item = wrapper.read(Types.ITEM1_13_2);\n            boolean signing = wrapper.read(Types.BOOLEAN);\n            wrapper.passthrough(Types.VAR_INT); // Slot comes first\n\n            if (item == null) {\n                wrapper.write(Types.VAR_INT, 0); // Pages length\n                wrapper.write(Types.BOOLEAN, false); // Optional title\n                return;\n            }\n\n            CompoundTag tag = item.tag();\n            ListTag<StringTag> pagesTag;\n            StringTag titleTag = null;\n            // Sanity checks\n            if (tag == null || (pagesTag = tag.getListTag(\"pages\", StringTag.class)) == null\n                || (signing && (titleTag = tag.getStringTag(\"title\")) == null)) {\n                wrapper.write(Types.VAR_INT, 0); // Pages length\n                wrapper.write(Types.BOOLEAN, false); // Optional title\n                return;\n            }\n\n            // Write pages - limit them first\n            if (pagesTag.size() > MAX_PAGES) {\n                pagesTag = new ListTag<>(pagesTag.getValue().subList(0, MAX_PAGES));\n            }\n\n            wrapper.write(Types.VAR_INT, pagesTag.size());\n            for (StringTag pageTag : pagesTag) {\n                String page = pageTag.getValue();\n                // Limit page length\n                if (page.length() > MAX_PAGE_LENGTH) {\n                    page = page.substring(0, MAX_PAGE_LENGTH);\n                }\n\n                wrapper.write(Types.STRING, page);\n            }\n\n            // Write optional title\n            wrapper.write(Types.BOOLEAN, signing);\n            if (signing) {\n                // Limit title length\n                String title = titleTag.getValue();\n                if (title.length() > MAX_TITLE_LENGTH) {\n                    title = title.substring(0, MAX_TITLE_LENGTH);\n                }\n\n                wrapper.write(Types.STRING, title);\n            }\n        });\n    }\n\n    @Override\n    public void init(UserConnection connection) {\n        connection.put(new InventoryStateIds());\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_17_1to1_17/storage/InventoryStateIds.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_17_1to1_17.storage;\n\nimport com.viaversion.viaversion.api.connection.StorableObject;\nimport com.viaversion.viaversion.libs.fastutil.ints.Int2IntMap;\nimport com.viaversion.viaversion.libs.fastutil.ints.Int2IntOpenHashMap;\n\npublic final class InventoryStateIds implements StorableObject {\n    private final Int2IntMap ids = new Int2IntOpenHashMap();\n\n    public InventoryStateIds() {\n        ids.defaultReturnValue(Integer.MAX_VALUE);\n    }\n\n    public void setStateId(int containerId, int id) {\n        ids.put(containerId, id);\n    }\n\n    public int removeStateId(int containerId) {\n        return ids.remove(containerId);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_17to1_16_4/Protocol1_17To1_16_4.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_17to1_16_4;\n\nimport com.viaversion.viabackwards.ViaBackwards;\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viabackwards.api.data.BackwardsMappingData;\nimport com.viaversion.viabackwards.api.rewriters.text.JsonNBTComponentRewriter;\nimport com.viaversion.viabackwards.protocol.v1_17to1_16_4.rewriter.BlockItemPacketRewriter1_17;\nimport com.viaversion.viabackwards.protocol.v1_17to1_16_4.rewriter.EntityPacketRewriter1_17;\nimport com.viaversion.viabackwards.protocol.v1_17to1_16_4.storage.PlayerLastCursorItem;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.RegistryType;\nimport com.viaversion.viaversion.api.minecraft.TagData;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_17;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.data.entity.EntityTrackerBase;\nimport com.viaversion.viaversion.libs.fastutil.ints.IntArrayList;\nimport com.viaversion.viaversion.libs.fastutil.ints.IntList;\nimport com.viaversion.viaversion.protocols.v1_16_1to1_16_2.packet.ClientboundPackets1_16_2;\nimport com.viaversion.viaversion.protocols.v1_16_1to1_16_2.packet.ServerboundPackets1_16_2;\nimport com.viaversion.viaversion.protocols.v1_16_4to1_17.Protocol1_16_4To1_17;\nimport com.viaversion.viaversion.protocols.v1_16_4to1_17.packet.ClientboundPackets1_17;\nimport com.viaversion.viaversion.protocols.v1_16_4to1_17.packet.ServerboundPackets1_17;\nimport com.viaversion.viaversion.rewriter.BlockRewriter;\nimport com.viaversion.viaversion.rewriter.IdRewriteFunction;\nimport com.viaversion.viaversion.rewriter.ParticleRewriter;\nimport com.viaversion.viaversion.rewriter.TagRewriter;\nimport com.viaversion.viaversion.rewriter.text.ComponentRewriterBase;\nimport com.viaversion.viaversion.util.Key;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\npublic final class Protocol1_17To1_16_4 extends BackwardsProtocol<ClientboundPackets1_17, ClientboundPackets1_16_2, ServerboundPackets1_17, ServerboundPackets1_16_2> {\n\n    public static final BackwardsMappingData MAPPINGS = new BackwardsMappingData(\"1.17\", \"1.16.2\", Protocol1_16_4To1_17.class);\n    private static final RegistryType[] TAG_REGISTRY_TYPES = {RegistryType.BLOCK, RegistryType.ITEM, RegistryType.FLUID, RegistryType.ENTITY};\n    private static final int[] EMPTY_ARRAY = {};\n    private final EntityPacketRewriter1_17 entityRewriter = new EntityPacketRewriter1_17(this);\n    private final BlockItemPacketRewriter1_17 blockItemPackets = new BlockItemPacketRewriter1_17(this);\n    private final ParticleRewriter<ClientboundPackets1_17> particleRewriter = new ParticleRewriter<>(this);\n    private final JsonNBTComponentRewriter<ClientboundPackets1_17> translatableRewriter = new JsonNBTComponentRewriter<>(this, ComponentRewriterBase.ReadType.JSON);\n    private final TagRewriter<ClientboundPackets1_17> tagRewriter = new TagRewriter<>(this);\n    private final BlockRewriter<ClientboundPackets1_17> blockRewriter = BlockRewriter.for1_14(this);\n\n    public Protocol1_17To1_16_4() {\n        super(ClientboundPackets1_17.class, ClientboundPackets1_16_2.class, ServerboundPackets1_17.class, ServerboundPackets1_16_2.class);\n    }\n\n    @Override\n    protected void registerPackets() {\n        super.registerPackets();\n\n        registerClientbound(ClientboundPackets1_17.UPDATE_TAGS, wrapper -> {\n            Map<String, List<TagData>> tags = new HashMap<>();\n\n            int length = wrapper.read(Types.VAR_INT);\n            for (int i = 0; i < length; i++) {\n                String resourceKey = Key.stripMinecraftNamespace(wrapper.read(Types.STRING));\n                List<TagData> tagList = new ArrayList<>();\n                tags.put(resourceKey, tagList);\n\n                int tagLength = wrapper.read(Types.VAR_INT);\n                for (int j = 0; j < tagLength; j++) {\n                    String identifier = wrapper.read(Types.STRING);\n                    int[] entries = wrapper.read(Types.VAR_INT_ARRAY_PRIMITIVE);\n                    tagList.add(new TagData(identifier, entries));\n                }\n            }\n\n            // Put them into the hardcoded order of Vanilla tags (and only those), rewrite ids\n            for (RegistryType type : TAG_REGISTRY_TYPES) {\n                List<TagData> tagList = tags.get(type.identifier());\n                if (tagList == null) {\n                    // Higher versions may not send the otherwise expected tags\n                    wrapper.write(Types.VAR_INT, 0);\n                    continue;\n                }\n\n                IdRewriteFunction rewriter = tagRewriter.getRewriter(type);\n\n                wrapper.write(Types.VAR_INT, tagList.size());\n                for (TagData tagData : tagList) {\n                    int[] entries = tagData.entries();\n                    if (rewriter != null) {\n                        // Handle id rewriting now\n                        IntList idList = new IntArrayList(entries.length);\n                        for (int id : entries) {\n                            int mappedId = rewriter.rewrite(id);\n                            if (mappedId != -1) {\n                                idList.add(mappedId);\n                            }\n                        }\n                        entries = idList.toArray(EMPTY_ARRAY);\n                    }\n\n                    wrapper.write(Types.STRING, tagData.identifier());\n                    wrapper.write(Types.VAR_INT_ARRAY_PRIMITIVE, entries);\n                }\n            }\n        });\n\n        registerClientbound(ClientboundPackets1_17.RESOURCE_PACK, wrapper -> {\n            wrapper.passthrough(Types.STRING);\n            wrapper.passthrough(Types.STRING);\n            wrapper.read(Types.BOOLEAN); // Required\n            wrapper.read(Types.OPTIONAL_COMPONENT); // Prompt message\n        });\n\n        registerClientbound(ClientboundPackets1_17.EXPLODE, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.FLOAT); // X\n                map(Types.FLOAT); // Y\n                map(Types.FLOAT); // Z\n                map(Types.FLOAT); // Strength\n                handler(wrapper -> {\n                    wrapper.write(Types.INT, wrapper.read(Types.VAR_INT)); // Collection length\n                });\n            }\n        });\n\n        registerClientbound(ClientboundPackets1_17.SET_DEFAULT_SPAWN_POSITION, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.BLOCK_POSITION1_14);\n                handler(wrapper -> {\n                    // Angle (which Mojang just forgot to write to the buffer, lol)\n                    wrapper.read(Types.FLOAT);\n                });\n            }\n        });\n\n        registerClientbound(ClientboundPackets1_17.PING, null, wrapper -> {\n            wrapper.cancel();\n\n            int id = wrapper.read(Types.INT);\n            short shortId = (short) id;\n            if (id == shortId && ViaBackwards.getConfig().handlePingsAsInvAcknowledgements()) {\n                // Send inventory acknowledgement to replace ping packet functionality in the unsigned byte range\n                PacketWrapper acknowledgementPacket = wrapper.create(ClientboundPackets1_16_2.CONTAINER_ACK);\n                acknowledgementPacket.write(Types.UNSIGNED_BYTE, (short) 0); // Inventory id\n                acknowledgementPacket.write(Types.SHORT, shortId); // Confirmation id\n                acknowledgementPacket.write(Types.BOOLEAN, false); // Accepted\n                acknowledgementPacket.send(Protocol1_17To1_16_4.class);\n                return;\n            }\n\n            // Plugins expecting a real response will have to handle this accordingly themselves\n            PacketWrapper pongPacket = wrapper.create(ServerboundPackets1_17.PONG);\n            pongPacket.write(Types.INT, id);\n            pongPacket.sendToServer(Protocol1_17To1_16_4.class);\n        });\n\n        registerServerbound(ServerboundPackets1_16_2.CLIENT_INFORMATION, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.STRING); // Locale\n                map(Types.BYTE); // View distance\n                map(Types.VAR_INT); // Chat mode\n                map(Types.BOOLEAN); // Chat colors\n                map(Types.UNSIGNED_BYTE); // Chat flags\n                map(Types.VAR_INT); // Main hand\n                handler(wrapper -> {\n                    wrapper.write(Types.BOOLEAN, false); // Text filtering\n                });\n            }\n        });\n\n        rewriteTitlePacket(ClientboundPackets1_17.SET_TITLE_TEXT, 0);\n        rewriteTitlePacket(ClientboundPackets1_17.SET_SUBTITLE_TEXT, 1);\n        rewriteTitlePacket(ClientboundPackets1_17.SET_ACTION_BAR_TEXT, 2);\n\n        mergePacket(ClientboundPackets1_17.SET_TITLES_ANIMATION, ClientboundPackets1_16_2.SET_TITLES, 3);\n        registerClientbound(ClientboundPackets1_17.CLEAR_TITLES, ClientboundPackets1_16_2.SET_TITLES, wrapper -> {\n            if (wrapper.read(Types.BOOLEAN)) {\n                wrapper.write(Types.VAR_INT, 5); // Reset times\n            } else {\n                wrapper.write(Types.VAR_INT, 4); // Simple clear\n            }\n        });\n\n        cancelClientbound(ClientboundPackets1_17.ADD_VIBRATION_SIGNAL);\n    }\n\n    @Override\n    public void init(UserConnection user) {\n        addEntityTracker(user, new EntityTrackerBase(user, EntityTypes1_17.PLAYER));\n        user.put(new PlayerLastCursorItem());\n    }\n\n    @Override\n    public BackwardsMappingData getMappingData() {\n        return MAPPINGS;\n    }\n\n    @Override\n    public JsonNBTComponentRewriter<ClientboundPackets1_17> getComponentRewriter() {\n        return translatableRewriter;\n    }\n\n    public void mergePacket(ClientboundPackets1_17 newPacketType, ClientboundPackets1_16_2 oldPacketType, int type) {\n        // A few packets that had different handling based on an initially read enum type were split into different ones\n        registerClientbound(newPacketType, oldPacketType, wrapper -> wrapper.write(Types.VAR_INT, type));\n    }\n\n    private void rewriteTitlePacket(ClientboundPackets1_17 newPacketType, int type) {\n        // Also handles translations in the title\n        registerClientbound(newPacketType, ClientboundPackets1_16_2.SET_TITLES, wrapper -> {\n            wrapper.write(Types.VAR_INT, type);\n            translatableRewriter.processText(wrapper.user(), wrapper.passthrough(Types.COMPONENT));\n        });\n    }\n\n    @Override\n    public EntityPacketRewriter1_17 getEntityRewriter() {\n        return entityRewriter;\n    }\n\n    @Override\n    public BlockItemPacketRewriter1_17 getItemRewriter() {\n        return blockItemPackets;\n    }\n\n    @Override\n    public BlockRewriter<ClientboundPackets1_17> getBlockRewriter() {\n        return blockRewriter;\n    }\n\n    @Override\n    public ParticleRewriter<ClientboundPackets1_17> getParticleRewriter() {\n        return particleRewriter;\n    }\n\n    @Override\n    public TagRewriter<ClientboundPackets1_17> getTagRewriter() {\n        return tagRewriter;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_17to1_16_4/data/MapColorMappings1_16_4.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_17to1_16_4.data;\n\nimport com.viaversion.viaversion.libs.fastutil.ints.Int2IntMap;\nimport com.viaversion.viaversion.libs.fastutil.ints.Int2IntOpenHashMap;\n\npublic final class MapColorMappings1_16_4 {\n\n    private static final Int2IntMap MAPPINGS = new Int2IntOpenHashMap();\n\n    static {\n        MAPPINGS.put(236, 85); // (70, 70, 70) -> (65, 65, 65)\n        MAPPINGS.put(237, 27); // (86, 86, 86) -> (88, 88, 88)\n        MAPPINGS.put(238, 45); // (100, 100, 100) -> (96, 96, 96)\n        MAPPINGS.put(239, 84); // (52, 52, 52) -> (53, 53, 53)\n        MAPPINGS.put(240, 144); // (152, 123, 103) -> (147, 124, 113)\n        MAPPINGS.put(241, 145); // (186, 150, 126) -> (180, 152, 138)\n        MAPPINGS.put(242, 146); // (216, 175, 147) -> (209, 177, 161)\n        MAPPINGS.put(243, 147); // (114, 92, 77) -> (110, 93, 85)\n        MAPPINGS.put(244, 127); // (89, 117, 105) -> (48, 115, 112)\n        MAPPINGS.put(245, 226); // (109, 144, 129) -> (58, 142, 140)\n        MAPPINGS.put(246, 124); // (127, 167, 150) -> (64, 154, 150)\n        MAPPINGS.put(247, 227); // (67, 88, 79) -> (30, 75, 74)\n    }\n\n    public static int getMappedColor(int color) {\n        return MAPPINGS.getOrDefault(color, -1);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_17to1_16_4/rewriter/BlockItemPacketRewriter1_17.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_17to1_16_4.rewriter;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.LongArrayTag;\nimport com.viaversion.nbt.tag.NumberTag;\nimport com.viaversion.nbt.tag.Tag;\nimport com.viaversion.viabackwards.ViaBackwards;\nimport com.viaversion.viabackwards.api.rewriters.BackwardsItemRewriter;\nimport com.viaversion.viabackwards.api.rewriters.MapColorRewriter;\nimport com.viaversion.viabackwards.protocol.v1_17to1_16_4.Protocol1_17To1_16_4;\nimport com.viaversion.viabackwards.protocol.v1_17to1_16_4.data.MapColorMappings1_16_4;\nimport com.viaversion.viabackwards.protocol.v1_17_1to1_17.storage.InventoryStateIds;\nimport com.viaversion.viabackwards.protocol.v1_17to1_16_4.storage.PlayerLastCursorItem;\nimport com.viaversion.viaversion.api.data.entity.EntityTracker;\nimport com.viaversion.viaversion.api.minecraft.BlockChangeRecord;\nimport com.viaversion.viaversion.api.minecraft.chunks.Chunk;\nimport com.viaversion.viaversion.api.minecraft.chunks.ChunkSection;\nimport com.viaversion.viaversion.api.minecraft.chunks.DataPalette;\nimport com.viaversion.viaversion.api.minecraft.chunks.PaletteType;\nimport com.viaversion.viaversion.api.minecraft.item.Item;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.chunk.ChunkType1_16_2;\nimport com.viaversion.viaversion.api.type.types.chunk.ChunkType1_17;\nimport com.viaversion.viaversion.protocols.v1_16_1to1_16_2.packet.ClientboundPackets1_16_2;\nimport com.viaversion.viaversion.protocols.v1_16_1to1_16_2.packet.ServerboundPackets1_16_2;\nimport com.viaversion.viaversion.protocols.v1_16_4to1_17.packet.ClientboundPackets1_17;\nimport com.viaversion.viaversion.protocols.v1_16_4to1_17.packet.ServerboundPackets1_17;\nimport com.viaversion.viaversion.rewriter.RecipeRewriter;\nimport com.viaversion.viaversion.util.CompactArrayUtil;\nimport com.viaversion.viaversion.util.MathUtil;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.BitSet;\nimport java.util.List;\n\npublic final class BlockItemPacketRewriter1_17 extends BackwardsItemRewriter<ClientboundPackets1_17, ServerboundPackets1_16_2, Protocol1_17To1_16_4> {\n\n    private static final int BEDROCK_BLOCK_STATE = 33;\n\n    public BlockItemPacketRewriter1_17(Protocol1_17To1_16_4 protocol) {\n        super(protocol, Types.ITEM1_13_2, Types.ITEM1_13_2_SHORT_ARRAY);\n    }\n\n    @Override\n    protected void registerPackets() {\n        new RecipeRewriter<>(protocol).register(ClientboundPackets1_17.UPDATE_RECIPES);\n\n        protocol.registerServerbound(ServerboundPackets1_16_2.EDIT_BOOK, wrapper -> handleItemToServer(wrapper.user(), wrapper.passthrough(Types.ITEM1_13_2)));\n\n        // TODO Since the carried and modified items are typically set incorrectly, the server sends unnecessary\n        // set slot packets after practically every window click, since it thinks the client and server\n        // inventories are desynchronized. Ideally, we want to store a replica of each container state, and update\n        // it appropriately for both serverbound and clientbound window packets, then fill in the carried\n        // and modified items array as appropriate here. That would be a ton of work and replicated vanilla code,\n        // and the hack below mitigates the worst side effects of this issue, which is an incorrect carried item\n        // sent to the client when a right/left click drag is started. It works, at least for now...\n        protocol.replaceServerbound(ServerboundPackets1_16_2.CONTAINER_CLICK, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.BYTE);\n                handler(wrapper -> {\n                    short slot = wrapper.passthrough(Types.SHORT); // Slot\n                    byte button = wrapper.passthrough(Types.BYTE); // Button\n                    wrapper.read(Types.SHORT); // Action id - removed\n                    int mode = wrapper.passthrough(Types.VAR_INT); // Mode\n                    Item clicked = handleItemToServer(wrapper.user(), wrapper.read(Types.ITEM1_13_2)); // Clicked item\n\n                    // The 1.17 client would check the entire inventory for changes before -> after a click\n                    // and send the changed slots here. We include the clicked slot so the server\n                    // detects any desync (e.g. cancelled InventoryClickEvent) and sends corrections.\n                    if (slot >= 0 && clicked != null) {\n                        wrapper.write(Types.VAR_INT, 1); // One modified slot\n                        wrapper.write(Types.SHORT, slot); // The clicked slot\n                        wrapper.write(Types.ITEM1_13_2, (Item) null); // Predicted: empty (item picked up)\n                    } else {\n                        wrapper.write(Types.VAR_INT, 0); // Empty array of slot+item\n                    }\n\n                    // Non-PICKUP modes (shift-click, swap, throw, drag, etc.) affect multiple slots\n                    // that we can't easily predict. Force a state ID mismatch so the server\n                    // sends a full inventory resync instead of individual slot corrections.\n                    if (mode != 0) {\n                        InventoryStateIds stateIds = wrapper.user().get(InventoryStateIds.class);\n                        if (stateIds != null) {\n                            stateIds.setStateId(wrapper.get(Types.BYTE, 0), -1);\n                        }\n                    }\n\n                    PlayerLastCursorItem state = wrapper.user().get(PlayerLastCursorItem.class);\n                    if (mode == 0 && button == 0 && clicked != null) {\n                        // Left click PICKUP\n                        // Carried item will (usually) be the entire clicked stack\n                        state.setLastCursorItem(clicked);\n                    } else if (mode == 0 && button == 1 && clicked != null) {\n                        // Right click PICKUP\n                        // Carried item will (usually) be half of the clicked stack (rounding up)\n                        // if the clicked slot is empty, otherwise it will (usually) be the whole clicked stack\n                        if (state.isSet()) {\n                            state.setLastCursorItem(clicked);\n                        } else {\n                            state.setLastCursorItem(clicked, (clicked.amount() + 1) / 2);\n                        }\n                    } else if (mode == 5 && slot == -999 && (button == 0 || button == 4)) {\n                        // Start drag (left or right click)\n                        // Preserve guessed carried item and forward to server\n                        // This mostly fixes the click and drag ghost item issue\n\n                        // no-op\n                    } else {\n                        // Carried item unknown (TODO)\n                        state.setLastCursorItem(null);\n                    }\n\n                    Item carried = state.getLastCursorItem();\n                    if (carried == null) {\n                        // Expected is the carried item after clicking, old clients send the clicked one (*mostly* being the same)\n                        wrapper.write(Types.ITEM1_13_2, clicked);\n                    } else {\n                        wrapper.write(Types.ITEM1_13_2, carried);\n                    }\n                });\n            }\n        });\n\n        protocol.replaceClientbound(ClientboundPackets1_17.CONTAINER_SET_SLOT, wrapper -> {\n            byte windowId = wrapper.passthrough(Types.BYTE);\n            short slot = wrapper.passthrough(Types.SHORT);\n\n            Item carried = wrapper.read(Types.ITEM1_13_2);\n            if (carried != null && windowId == -1 && slot == -1) {\n                // This is related to the hack to fix click and drag ghost items above.\n                // After a completed drag, we have no idea how many items remain on the cursor,\n                // and vanilla logic replication would be required to calculate the value.\n                // When the click drag complete packet is sent, we will send an incorrect\n                // carried item, and the server will helpfully send this packet allowing us\n                // to update the internal state. This is necessary for fixing multiple sequential\n                // click drag actions without intermittent pickup actions.\n                wrapper.user().get(PlayerLastCursorItem.class).setLastCursorItem(carried);\n            }\n\n            wrapper.write(Types.ITEM1_13_2, handleItemToClient(wrapper.user(), carried));\n        });\n\n        protocol.registerServerbound(ServerboundPackets1_16_2.CONTAINER_ACK, null, wrapper -> {\n            wrapper.cancel();\n            if (!ViaBackwards.getConfig().handlePingsAsInvAcknowledgements()) {\n                return;\n            }\n\n            // Handle ping packet replacement\n            byte inventoryId = wrapper.read(Types.BYTE);\n            short confirmationId = wrapper.read(Types.SHORT);\n            boolean accepted = wrapper.read(Types.BOOLEAN);\n            if (inventoryId == 0 && accepted) {\n                PacketWrapper pongPacket = wrapper.create(ServerboundPackets1_17.PONG);\n                pongPacket.write(Types.INT, (int) confirmationId);\n                pongPacket.sendToServer(Protocol1_17To1_16_4.class);\n            }\n        });\n\n        protocol.replaceClientbound(ClientboundPackets1_17.LEVEL_PARTICLES, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.INT); // Particle id\n                map(Types.BOOLEAN); // Long distance\n                map(Types.DOUBLE); // X\n                map(Types.DOUBLE); // Y\n                map(Types.DOUBLE); // Z\n                map(Types.FLOAT); // Offset X\n                map(Types.FLOAT); // Offset Y\n                map(Types.FLOAT); // Offset Z\n                map(Types.FLOAT); // Particle data\n                map(Types.INT); // Particle count\n                handler(wrapper -> {\n                    int id = wrapper.get(Types.INT, 0);\n                    if (id == 16) {\n                        wrapper.passthrough(Types.FLOAT); // R\n                        wrapper.passthrough(Types.FLOAT); // G\n                        wrapper.passthrough(Types.FLOAT); // B\n                        wrapper.passthrough(Types.FLOAT); // Scale\n\n                        // Dust color transition -> Dust\n                        wrapper.read(Types.FLOAT); // R\n                        wrapper.read(Types.FLOAT); // G\n                        wrapper.read(Types.FLOAT); // B\n                    } else if (id == 37) {\n                        // Vibration signal - no nice mapping possible without tracking entity positions and doing particle tasks\n                        wrapper.set(Types.INT, 0, -1);\n                        wrapper.cancel();\n                    }\n                });\n                handler(protocol.getParticleRewriter().levelParticlesHandler1_13(Types.INT));\n            }\n        });\n\n        protocol.mergePacket(ClientboundPackets1_17.SET_BORDER_SIZE, ClientboundPackets1_16_2.SET_BORDER, 0);\n        protocol.mergePacket(ClientboundPackets1_17.SET_BORDER_LERP_SIZE, ClientboundPackets1_16_2.SET_BORDER, 1);\n        protocol.mergePacket(ClientboundPackets1_17.SET_BORDER_CENTER, ClientboundPackets1_16_2.SET_BORDER, 2);\n        protocol.mergePacket(ClientboundPackets1_17.INITIALIZE_BORDER, ClientboundPackets1_16_2.SET_BORDER, 3);\n        protocol.mergePacket(ClientboundPackets1_17.SET_BORDER_WARNING_DELAY, ClientboundPackets1_16_2.SET_BORDER, 4);\n        protocol.mergePacket(ClientboundPackets1_17.SET_BORDER_WARNING_DISTANCE, ClientboundPackets1_16_2.SET_BORDER, 5);\n\n        // The Great Shrunkening\n        // Chunk sections *will* be lost ¯\\_(ツ)_/¯\n        protocol.registerClientbound(ClientboundPackets1_17.LIGHT_UPDATE, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT); // X\n                map(Types.VAR_INT); // Z\n                map(Types.BOOLEAN); // Trust edges\n                handler(wrapper -> {\n                    EntityTracker tracker = wrapper.user().getEntityTracker(Protocol1_17To1_16_4.class);\n                    int startFromSection = Math.max(0, -(tracker.currentMinY() >> 4));\n\n                    long[] skyLightMask = wrapper.read(Types.LONG_ARRAY_PRIMITIVE);\n                    long[] blockLightMask = wrapper.read(Types.LONG_ARRAY_PRIMITIVE);\n                    int cutSkyLightMask = cutLightMask(skyLightMask, startFromSection);\n                    int cutBlockLightMask = cutLightMask(blockLightMask, startFromSection);\n                    wrapper.write(Types.VAR_INT, cutSkyLightMask);\n                    wrapper.write(Types.VAR_INT, cutBlockLightMask);\n\n                    long[] emptySkyLightMask = wrapper.read(Types.LONG_ARRAY_PRIMITIVE);\n                    long[] emptyBlockLightMask = wrapper.read(Types.LONG_ARRAY_PRIMITIVE);\n                    wrapper.write(Types.VAR_INT, cutLightMask(emptySkyLightMask, startFromSection));\n                    wrapper.write(Types.VAR_INT, cutLightMask(emptyBlockLightMask, startFromSection));\n\n                    writeLightArrays(wrapper, BitSet.valueOf(skyLightMask), cutSkyLightMask, startFromSection, tracker.currentWorldSectionHeight());\n                    writeLightArrays(wrapper, BitSet.valueOf(blockLightMask), cutBlockLightMask, startFromSection, tracker.currentWorldSectionHeight());\n                });\n            }\n\n            private void writeLightArrays(PacketWrapper wrapper, BitSet bitMask, int cutBitMask,\n                                          int startFromSection, int sectionHeight) {\n                int packetContentsLength = wrapper.read(Types.VAR_INT);\n                int read = 0;\n                List<byte[]> light = new ArrayList<>();\n\n                // Remove lower bounds\n                for (int i = 0; i < startFromSection; i++) {\n                    if (bitMask.get(i)) {\n                        read++;\n                        wrapper.read(Types.BYTE_ARRAY_PRIMITIVE);\n                    }\n                }\n\n                // Add the important 18 sections\n                for (int i = 0; i < 18; i++) {\n                    if (isSet(cutBitMask, i)) {\n                        read++;\n                        light.add(wrapper.read(Types.BYTE_ARRAY_PRIMITIVE));\n                    }\n                }\n\n                // Remove upper bounds\n                for (int i = startFromSection + 18; i < sectionHeight + 2; i++) {\n                    if (bitMask.get(i)) {\n                        read++;\n                        wrapper.read(Types.BYTE_ARRAY_PRIMITIVE);\n                    }\n                }\n\n                if (read != packetContentsLength) {\n                    // Read the rest if the server for some reason sends more than are actually consumed\n                    for (int i = read; i < packetContentsLength; i++) {\n                        wrapper.read(Types.BYTE_ARRAY_PRIMITIVE);\n                    }\n                }\n\n                // Aaand we're done\n                for (byte[] bytes : light) {\n                    wrapper.write(Types.BYTE_ARRAY_PRIMITIVE, bytes);\n                }\n            }\n\n            private boolean isSet(int mask, int i) {\n                return (mask & (1 << i)) != 0;\n            }\n        });\n\n        protocol.replaceClientbound(ClientboundPackets1_17.SECTION_BLOCKS_UPDATE, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.LONG); // Chunk pos\n                map(Types.BOOLEAN); // Suppress light updates\n                handler((wrapper) -> {\n                    // Remove sections below y 0 and above 255\n                    long chunkPos = wrapper.get(Types.LONG, 0);\n                    int chunkY = (int) (chunkPos << 44 >> 44);\n                    if (chunkY < 0 || chunkY > 15) {\n                        wrapper.cancel();\n                        return;\n                    }\n\n                    BlockChangeRecord[] records = wrapper.passthrough(Types.VAR_LONG_BLOCK_CHANGE_ARRAY);\n                    for (BlockChangeRecord record : records) {\n                        if (ViaBackwards.getConfig().bedrockAtY0() && chunkY == 0 && record.getSectionY() == 0) {\n                            record.setBlockId(BEDROCK_BLOCK_STATE);\n                        } else {\n                            record.setBlockId(protocol.getMappingData().getNewBlockStateId(record.getBlockId()));\n                        }\n                    }\n                });\n            }\n        });\n\n        protocol.replaceClientbound(ClientboundPackets1_17.BLOCK_UPDATE, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.BLOCK_POSITION1_14);\n                map(Types.VAR_INT);\n                handler((wrapper) -> {\n                    int y = wrapper.get(Types.BLOCK_POSITION1_14, 0).y();\n                    if (y < 0 || y > 255) {\n                        wrapper.cancel();\n                        return;\n                    }\n\n                    if (ViaBackwards.getConfig().bedrockAtY0() && y == 0) {\n                        wrapper.set(Types.VAR_INT, 0, BEDROCK_BLOCK_STATE);\n                    } else {\n                        wrapper.set(Types.VAR_INT, 0, protocol.getMappingData().getNewBlockStateId(wrapper.get(Types.VAR_INT, 0)));\n                    }\n                });\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_17.LEVEL_CHUNK, wrapper -> {\n            EntityTracker tracker = wrapper.user().getEntityTracker(Protocol1_17To1_16_4.class);\n            int currentWorldSectionHeight = tracker.currentWorldSectionHeight();\n\n            Chunk chunk = wrapper.read(new ChunkType1_17(currentWorldSectionHeight));\n            wrapper.write(ChunkType1_16_2.TYPE, chunk);\n\n            // Cut sections\n            int startFromSection = Math.max(0, -(tracker.currentMinY() >> 4));\n            chunk.setBiomeData(Arrays.copyOfRange(chunk.getBiomeData(), startFromSection * 64, (startFromSection * 64) + 1024));\n\n            chunk.setBitmask(cutMask(chunk.getChunkMask(), startFromSection, false));\n            chunk.setChunkMask(null);\n\n            ChunkSection[] sections = Arrays.copyOfRange(chunk.getSections(), startFromSection, startFromSection + 16);\n            chunk.setSections(sections);\n\n            CompoundTag heightMaps = chunk.getHeightMap();\n            for (Tag heightMapTag : heightMaps.values()) {\n                if (!(heightMapTag instanceof final LongArrayTag heightMap)) {\n                    continue; // Client can handle bad data\n                }\n\n                int[] heightMapData = new int[256];\n                int bitsPerEntry = MathUtil.ceilLog2((currentWorldSectionHeight << 4) + 1);\n                // Shift back to 0 based and clamp to normal height with 9 bits\n                CompactArrayUtil.iterateCompactArrayWithPadding(bitsPerEntry, heightMapData.length, heightMap.getValue(), (i, v) -> heightMapData[i] = MathUtil.clamp(v + tracker.currentMinY(), 0, 255));\n                heightMap.setValue(CompactArrayUtil.createCompactArrayWithPadding(9, heightMapData.length, i -> heightMapData[i]));\n            }\n\n            protocol.getBlockRewriter().handleChunk(chunk);\n\n            if (ViaBackwards.getConfig().bedrockAtY0()) {\n                final ChunkSection lowestSection = chunk.getSections()[0];\n                if (lowestSection != null) {\n                    final DataPalette blocks = lowestSection.palette(PaletteType.BLOCKS);\n                    for (int x = 0; x < 16; x++) {\n                        for (int z = 0; z < 16; z++) {\n                            blocks.setIdAt(x, 0, z, BEDROCK_BLOCK_STATE);\n                        }\n                    }\n                }\n            }\n\n            chunk.getBlockEntities().removeIf(compound -> {\n                NumberTag tag = compound.getNumberTag(\"y\");\n                if (tag == null) {\n                    return false;\n                }\n\n                final int y = tag.asInt();\n                return y < 0 || y > 255 || (ViaBackwards.getConfig().bedrockAtY0() && y == 0);\n            });\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_17.BLOCK_ENTITY_DATA, wrapper -> {\n            int y = wrapper.passthrough(Types.BLOCK_POSITION1_14).y();\n            if (y < 0 || y > 255 || (ViaBackwards.getConfig().bedrockAtY0() && y == 0)) {\n                wrapper.cancel();\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_17.BLOCK_DESTRUCTION, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT);\n                handler(wrapper -> {\n                    int y = wrapper.passthrough(Types.BLOCK_POSITION1_14).y();\n                    if (y < 0 || y > 255 || (ViaBackwards.getConfig().bedrockAtY0() && y == 0)) {\n                        wrapper.cancel();\n                    }\n                });\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_17.MAP_ITEM_DATA, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT); // Map ID\n                map(Types.BYTE); // Scale\n                handler(wrapper -> wrapper.write(Types.BOOLEAN, true)); // Tracking position\n                map(Types.BOOLEAN); // Locked\n                handler(wrapper -> {\n                    final int iconCount;\n                    final boolean hasMarkers = wrapper.read(Types.BOOLEAN);\n                    if (hasMarkers) {\n                        iconCount = wrapper.passthrough(Types.VAR_INT);\n                    } else {\n                        wrapper.write(Types.VAR_INT, 0); // Add icon count\n                        iconCount = 0;\n                    }\n                    MapColorRewriter.rewriteMapColors(wrapper, MapColorMappings1_16_4::getMappedColor, iconCount);\n                });\n            }\n        });\n    }\n\n    private int cutLightMask(long[] mask, int startFromSection) {\n        if (mask.length == 0) return 0;\n        return cutMask(BitSet.valueOf(mask), startFromSection, true);\n    }\n\n    private int cutMask(BitSet mask, int startFromSection, boolean lightMask) {\n        int cutMask = 0;\n        // Light masks have a section below and above the 16 main sections\n        int to = startFromSection + (lightMask ? 18 : 16);\n        for (int i = startFromSection, j = 0; i < to; i++, j++) {\n            if (mask.get(i)) {\n                cutMask |= (1 << j);\n            }\n        }\n        return cutMask;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_17to1_16_4/rewriter/EntityPacketRewriter1_17.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_17to1_16_4.rewriter;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.ListTag;\nimport com.viaversion.nbt.tag.NumberTag;\nimport com.viaversion.nbt.tag.StringTag;\nimport com.viaversion.viabackwards.ViaBackwards;\nimport com.viaversion.viabackwards.api.rewriters.EntityRewriter;\nimport com.viaversion.viabackwards.protocol.v1_17to1_16_4.Protocol1_17To1_16_4;\nimport com.viaversion.viaversion.api.minecraft.Particle;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityType;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_16_2;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_17;\nimport com.viaversion.viaversion.api.minecraft.entitydata.EntityDataType;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.version.Types1_16;\nimport com.viaversion.viaversion.api.type.types.version.Types1_17;\nimport com.viaversion.viaversion.protocols.v1_16_1to1_16_2.packet.ClientboundPackets1_16_2;\nimport com.viaversion.viaversion.protocols.v1_16_4to1_17.packet.ClientboundPackets1_17;\nimport com.viaversion.viaversion.util.TagUtil;\n\npublic final class EntityPacketRewriter1_17 extends EntityRewriter<ClientboundPackets1_17, Protocol1_17To1_16_4> {\n\n    private boolean warned = ViaBackwards.getConfig().bedrockAtY0() || ViaBackwards.getConfig().suppressEmulationWarnings();\n\n    public EntityPacketRewriter1_17(Protocol1_17To1_16_4 protocol) {\n        super(protocol);\n    }\n\n    @Override\n    protected void registerPackets() {\n        registerTracker(ClientboundPackets1_17.ADD_EXPERIENCE_ORB, EntityTypes1_17.EXPERIENCE_ORB);\n        registerTracker(ClientboundPackets1_17.ADD_PAINTING, EntityTypes1_17.PAINTING);\n        registerTracker(ClientboundPackets1_17.ADD_PLAYER, EntityTypes1_17.PLAYER);\n        registerSetEntityData(ClientboundPackets1_17.SET_ENTITY_DATA, Types1_17.ENTITY_DATA_LIST, Types1_16.ENTITY_DATA_LIST);\n\n        protocol.appendClientbound(ClientboundPackets1_17.ADD_ENTITY, wrapper -> {\n            final int entityType = wrapper.get(Types.VAR_INT, 1);\n            if (entityType != EntityTypes1_16_2.ITEM_FRAME.getId()) {\n                return;\n            }\n\n            // Older clients will ignore the data field and the server sets the item frame rotation by the yaw/pitch field inside the packet,\n            // newer clients do the opposite and ignore yaw/pitch and use the data field from the packet.\n            final int data = wrapper.get(Types.INT, 0);\n\n            float pitch = 0F;\n            float yaw = 0F;\n            switch (Math.abs(data % 6)) {\n                case 0 /* down */ -> pitch = 90F;\n                case 1 /* up */ -> pitch = -90F;\n                case 2 /* north */ -> yaw = 180F;\n                case 4 /* west */ -> yaw = 90F;\n                case 5 /* east */ -> yaw = 270;\n            }\n            wrapper.set(Types.BYTE, 0, (byte) (pitch * 256F / 360F));\n            wrapper.set(Types.BYTE, 1, (byte) (yaw * 256F / 360F));\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_17.REMOVE_ENTITY, ClientboundPackets1_16_2.REMOVE_ENTITIES, wrapper -> {\n            int entityId = wrapper.read(Types.VAR_INT);\n            tracker(wrapper.user()).removeEntity(entityId);\n\n            // Write into single value array\n            int[] array = {entityId};\n            wrapper.write(Types.VAR_INT_ARRAY_PRIMITIVE, array);\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_17.LOGIN, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.INT); // Entity ID\n                map(Types.BOOLEAN); // Hardcore\n                map(Types.BYTE); // Gamemode\n                map(Types.BYTE); // Previous Gamemode\n                map(Types.STRING_ARRAY); // Worlds\n                map(Types.NAMED_COMPOUND_TAG); // Dimension registry\n                map(Types.NAMED_COMPOUND_TAG); // Current dimension data\n                map(Types.STRING); // World\n                handler(wrapper -> {\n                    byte previousGamemode = wrapper.get(Types.BYTE, 1);\n                    if (previousGamemode == -1) { // \"Unset\" gamemode removed\n                        wrapper.set(Types.BYTE, 1, (byte) 0);\n                    }\n                });\n                handler(getPlayerTrackerHandler());\n                handler(worldDataTrackerHandler(1));\n                handler(wrapper -> {\n                    CompoundTag registry = wrapper.get(Types.NAMED_COMPOUND_TAG, 0);\n                    ListTag<CompoundTag> biomes = TagUtil.getRegistryEntries(registry, \"worldgen/biome\");\n                    for (CompoundTag biome : biomes) {\n                        CompoundTag biomeCompound = biome.getCompoundTag(\"element\");\n                        StringTag category = biomeCompound.getStringTag(\"category\");\n                        if (category.getValue().equalsIgnoreCase(\"underground\")) {\n                            category.setValue(\"none\");\n                        }\n                    }\n\n                    ListTag<CompoundTag> dimensions = TagUtil.getRegistryEntries(registry, \"dimension_type\");\n                    for (CompoundTag dimension : dimensions) {\n                        CompoundTag dimensionCompound = dimension.getCompoundTag(\"element\");\n                        reduceExtendedHeight(dimensionCompound, false);\n                    }\n\n                    reduceExtendedHeight(wrapper.get(Types.NAMED_COMPOUND_TAG, 1), true);\n                });\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_17.RESPAWN, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.NAMED_COMPOUND_TAG); // Dimension data\n                map(Types.STRING); // World\n                handler(worldDataTrackerHandler(0));\n                handler(wrapper -> reduceExtendedHeight(wrapper.get(Types.NAMED_COMPOUND_TAG, 0), true));\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_17.PLAYER_POSITION, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.DOUBLE);\n                map(Types.DOUBLE);\n                map(Types.DOUBLE);\n                map(Types.FLOAT);\n                map(Types.FLOAT);\n                map(Types.BYTE);\n                map(Types.VAR_INT);\n                read(Types.BOOLEAN); // Dismount vehicle ¯\\_(ツ)_/¯\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_17.UPDATE_ATTRIBUTES, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT); // Entity id\n                handler(wrapper -> wrapper.write(Types.INT, wrapper.read(Types.VAR_INT))); // Collection length\n            }\n        });\n\n        protocol.mergePacket(ClientboundPackets1_17.PLAYER_COMBAT_ENTER, ClientboundPackets1_16_2.PLAYER_COMBAT, 0);\n        protocol.mergePacket(ClientboundPackets1_17.PLAYER_COMBAT_END, ClientboundPackets1_16_2.PLAYER_COMBAT, 1);\n        protocol.registerClientbound(ClientboundPackets1_17.PLAYER_COMBAT_KILL, ClientboundPackets1_16_2.PLAYER_COMBAT, wrapper -> {\n            wrapper.write(Types.VAR_INT, 2);\n\n            wrapper.passthrough(Types.VAR_INT); // Duration/Player id\n            wrapper.passthrough(Types.INT); // Killer id\n            protocol.getComponentRewriter().processText(wrapper.user(), wrapper.passthrough(Types.COMPONENT));\n        });\n    }\n\n    @Override\n    protected void registerRewrites() {\n        filter().handler((event, data) -> {\n            data.setDataType(Types1_16.ENTITY_DATA_TYPES.byId(data.dataType().typeId()));\n\n            EntityDataType type = data.dataType();\n            if (type == Types1_16.ENTITY_DATA_TYPES.particleType) {\n                Particle particle = (Particle) data.getValue();\n                if (particle.id() == 16) { // Dust / Dust Transition\n                    // Remove transition target color values 4-6\n                    particle.getArguments().subList(4, 7).clear();\n                } else if (particle.id() == 37) { // Vibration Signal\n                    // No nice mapping possible without tracking entity positions and doing particle tasks\n                    particle.setId(0);\n                    particle.getArguments().clear();\n                    return;\n                }\n\n                protocol.getParticleRewriter().rewriteParticle(event.user(), particle);\n            } else if (type == Types1_16.ENTITY_DATA_TYPES.poseType) {\n                // Goat LONG_JUMP added at 6\n                int pose = data.value();\n                if (pose == 6) {\n                    data.setValue(1); // FALL_FLYING\n                } else if (pose > 6) {\n                    data.setValue(pose - 1);\n                }\n            }\n        });\n\n        // Particles have already been handled\n        registerEntityDataTypeHandler(Types1_16.ENTITY_DATA_TYPES.itemType, null, Types1_16.ENTITY_DATA_TYPES.optionalBlockStateType, null,\n            Types1_16.ENTITY_DATA_TYPES.componentType, Types1_16.ENTITY_DATA_TYPES.optionalComponentType);\n\n        filter().type(EntityTypes1_17.AXOLOTL).cancel(17);\n        filter().type(EntityTypes1_17.AXOLOTL).cancel(18);\n        filter().type(EntityTypes1_17.AXOLOTL).cancel(19);\n\n        filter().type(EntityTypes1_17.GLOW_SQUID).cancel(16);\n\n        filter().type(EntityTypes1_17.GOAT).cancel(17);\n\n        filter().type(EntityTypes1_17.SHULKER).addIndex(17); // TODO Handle attachment pos?\n\n        registerBlockStateHandler(EntityTypes1_17.ABSTRACT_MINECART, 11);\n\n        filter().removeIndex(7); // Ticks frozen\n    }\n\n    @Override\n    public void onMappingDataLoaded() {\n        super.onMappingDataLoaded();\n        mapEntityTypeWithData(EntityTypes1_17.AXOLOTL, EntityTypes1_17.TROPICAL_FISH).jsonName();\n        mapEntityTypeWithData(EntityTypes1_17.GOAT, EntityTypes1_17.SHEEP).jsonName();\n        mapEntityTypeWithData(EntityTypes1_17.GLOW_SQUID, EntityTypes1_17.SQUID).jsonName();\n        mapEntityTypeWithData(EntityTypes1_17.GLOW_ITEM_FRAME, EntityTypes1_17.ITEM_FRAME);\n    }\n\n    @Override\n    public EntityType typeFromId(int typeId) {\n        return EntityTypes1_17.getTypeFromId(typeId);\n    }\n\n    private void reduceExtendedHeight(CompoundTag tag, boolean warn) {\n        NumberTag minY = tag.getNumberTag(\"min_y\");\n        NumberTag height = tag.getNumberTag(\"height\");\n        NumberTag logicalHeight = tag.getNumberTag(\"logical_height\");\n        if (minY.asInt() != 0 || height.asInt() > 256 || logicalHeight.asInt() > 256) {\n            if (warn && !warned) {\n                protocol.getLogger().warning(\"Increased world height is NOT SUPPORTED for 1.16 players and below. They will see a void below y 0 and above 256. You can enable the `bedrock-at-y-0` config option to replace the air with a bedrock layer.\");\n                warned = true;\n            }\n\n            tag.putInt(\"height\", Math.min(256, height.asInt()));\n            tag.putInt(\"logical_height\", Math.min(256, logicalHeight.asInt()));\n        }\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_17to1_16_4/storage/PlayerLastCursorItem.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_17to1_16_4.storage;\n\nimport com.viaversion.viaversion.api.connection.StorableObject;\nimport com.viaversion.viaversion.api.minecraft.item.Item;\n\npublic class PlayerLastCursorItem implements StorableObject {\n    private Item lastCursorItem;\n\n    public Item getLastCursorItem() {\n        return copyItem(lastCursorItem);\n    }\n\n    public void setLastCursorItem(Item item) {\n        this.lastCursorItem = copyItem(item);\n    }\n\n    public void setLastCursorItem(Item item, int amount) {\n        this.lastCursorItem = copyItem(item);\n        this.lastCursorItem.setAmount(amount);\n    }\n\n    public boolean isSet() {\n        return lastCursorItem != null;\n    }\n\n    private static Item copyItem(Item item) {\n        if (item == null) {\n            return null;\n        }\n        return item.copy();\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_18_2to1_18/Protocol1_18_2To1_18.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_18_2to1_18;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.ListTag;\nimport com.viaversion.nbt.tag.StringTag;\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viabackwards.protocol.v1_18_2to1_18.rewriter.CommandRewriter1_18_2;\nimport com.viaversion.viaversion.api.Via;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandler;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.protocols.v1_16_4to1_17.packet.ServerboundPackets1_17;\nimport com.viaversion.viaversion.protocols.v1_17_1to1_18.packet.ClientboundPackets1_18;\nimport com.viaversion.viaversion.util.TagUtil;\n\npublic final class Protocol1_18_2To1_18 extends BackwardsProtocol<ClientboundPackets1_18, ClientboundPackets1_18, ServerboundPackets1_17, ServerboundPackets1_17> {\n\n    public Protocol1_18_2To1_18() {\n        super(ClientboundPackets1_18.class, ClientboundPackets1_18.class, ServerboundPackets1_17.class, ServerboundPackets1_17.class);\n    }\n\n    @Override\n    protected void registerPackets() {\n        new CommandRewriter1_18_2(this).registerDeclareCommands(ClientboundPackets1_18.COMMANDS);\n\n        final PacketHandler entityEffectIdHandler = wrapper -> {\n            final int id = wrapper.read(Types.VAR_INT);\n            if ((byte) id != id) {\n                if (Via.getConfig().logOtherConversionWarnings()) {\n                    getLogger().warning(\"Cannot send entity effect id \" + id + \" to old client\");\n                }\n                wrapper.cancel();\n                return;\n            }\n\n            wrapper.write(Types.BYTE, (byte) id);\n        };\n        registerClientbound(ClientboundPackets1_18.UPDATE_MOB_EFFECT, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT); // Entity id\n                handler(entityEffectIdHandler);\n            }\n        });\n\n        registerClientbound(ClientboundPackets1_18.REMOVE_MOB_EFFECT, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT); // Entity id\n                handler(entityEffectIdHandler);\n            }\n        });\n\n        registerClientbound(ClientboundPackets1_18.LOGIN, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.INT); // Entity ID\n                map(Types.BOOLEAN); // Hardcore\n                map(Types.BYTE); // Gamemode\n                map(Types.BYTE); // Previous Gamemode\n                map(Types.STRING_ARRAY); // World List\n                map(Types.NAMED_COMPOUND_TAG); // Registry\n                map(Types.NAMED_COMPOUND_TAG); // Current dimension data\n                handler(wrapper -> {\n                    final CompoundTag registry = wrapper.get(Types.NAMED_COMPOUND_TAG, 0);\n                    final ListTag<CompoundTag> dimensions = TagUtil.getRegistryEntries(registry, \"dimension_type\");\n                    for (final CompoundTag dimension : dimensions) {\n                        removeTagPrefix(dimension.getCompoundTag(\"element\"));\n                    }\n\n                    removeTagPrefix(wrapper.get(Types.NAMED_COMPOUND_TAG, 1));\n                });\n            }\n        });\n\n        registerClientbound(ClientboundPackets1_18.RESPAWN, wrapper -> removeTagPrefix(wrapper.passthrough(Types.NAMED_COMPOUND_TAG)));\n    }\n\n    private void removeTagPrefix(CompoundTag tag) {\n        final StringTag infiniburnTag = tag.getStringTag(\"infiniburn\");\n        if (infiniburnTag != null) {\n            infiniburnTag.setValue(infiniburnTag.getValue().substring(1));\n        }\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_18_2to1_18/rewriter/CommandRewriter1_18_2.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_18_2to1_18.rewriter;\n\nimport com.viaversion.viabackwards.protocol.v1_18_2to1_18.Protocol1_18_2To1_18;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.protocols.v1_17_1to1_18.packet.ClientboundPackets1_18;\nimport com.viaversion.viaversion.rewriter.CommandRewriter;\nimport org.checkerframework.checker.nullness.qual.Nullable;\n\npublic final class CommandRewriter1_18_2 extends CommandRewriter<ClientboundPackets1_18> {\n\n    public CommandRewriter1_18_2(Protocol1_18_2To1_18 protocol) {\n        super(protocol);\n\n        // Uncompletable without the full list\n        this.parserHandlers.put(\"minecraft:resource\", wrapper -> {\n            wrapper.read(Types.STRING);\n            wrapper.write(Types.VAR_INT, 1); // Quotable string\n        });\n        this.parserHandlers.put(\"minecraft:resource_or_tag\", wrapper -> {\n            wrapper.read(Types.STRING);\n            wrapper.write(Types.VAR_INT, 1); // Quotable string\n        });\n    }\n\n    @Override\n    public @Nullable String handleArgumentType(String argumentType) {\n        if (argumentType.equals(\"minecraft:resource\") || argumentType.equals(\"minecraft:resource_or_tag\")) {\n            return \"brigadier:string\";\n        }\n        return super.handleArgumentType(argumentType);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_18to1_17_1/Protocol1_18To1_17_1.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_18to1_17_1;\n\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viabackwards.api.rewriters.SoundRewriter;\nimport com.viaversion.viabackwards.api.rewriters.text.JsonNBTComponentRewriter;\nimport com.viaversion.viabackwards.protocol.v1_18to1_17_1.data.BackwardsMappingData1_18;\nimport com.viaversion.viabackwards.protocol.v1_18to1_17_1.rewriter.BlockItemPacketRewriter1_18;\nimport com.viaversion.viabackwards.protocol.v1_18to1_17_1.rewriter.EntityPacketRewriter1_18;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.RegistryType;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_17;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandler;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.data.entity.EntityTrackerBase;\nimport com.viaversion.viaversion.protocols.v1_16_4to1_17.packet.ServerboundPackets1_17;\nimport com.viaversion.viaversion.protocols.v1_17_1to1_18.packet.ClientboundPackets1_18;\nimport com.viaversion.viaversion.protocols.v1_17to1_17_1.packet.ClientboundPackets1_17_1;\nimport com.viaversion.viaversion.rewriter.ParticleRewriter;\nimport com.viaversion.viaversion.rewriter.TagRewriter;\nimport com.viaversion.viaversion.rewriter.text.ComponentRewriterBase;\n\npublic final class Protocol1_18To1_17_1 extends BackwardsProtocol<ClientboundPackets1_18, ClientboundPackets1_17_1, ServerboundPackets1_17, ServerboundPackets1_17> {\n\n    private static final BackwardsMappingData1_18 MAPPINGS = new BackwardsMappingData1_18();\n    private final EntityPacketRewriter1_18 entityRewriter = new EntityPacketRewriter1_18(this);\n    private final BlockItemPacketRewriter1_18 itemRewriter = new BlockItemPacketRewriter1_18(this);\n    private final ParticleRewriter<ClientboundPackets1_18> particleRewriter = new ParticleRewriter<>(this);\n    private final JsonNBTComponentRewriter<ClientboundPackets1_18> translatableRewriter = new JsonNBTComponentRewriter<>(this, ComponentRewriterBase.ReadType.JSON);\n    private final TagRewriter<ClientboundPackets1_18> tagRewriter = new TagRewriter<>(this);\n\n    public Protocol1_18To1_17_1() {\n        super(ClientboundPackets1_18.class, ClientboundPackets1_17_1.class, ServerboundPackets1_17.class, ServerboundPackets1_17.class);\n    }\n\n    @Override\n    protected void registerPackets() {\n        super.registerPackets();\n\n        tagRewriter.addEmptyTag(RegistryType.BLOCK, \"minecraft:lava_pool_stone_replaceables\");\n\n        registerServerbound(ServerboundPackets1_17.CLIENT_INFORMATION, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.STRING); // Language\n                map(Types.BYTE); // View distance\n                map(Types.VAR_INT); // Chat visibility\n                map(Types.BOOLEAN); // Chat colors\n                map(Types.UNSIGNED_BYTE); // Model customization\n                map(Types.VAR_INT); // Main hand\n                map(Types.BOOLEAN); // Text filtering enabled\n                create(Types.BOOLEAN, true); // Allow listing in server list preview\n            }\n        });\n\n        appendClientbound(ClientboundPackets1_18.SET_OBJECTIVE, cutName(0, 16));\n        registerClientbound(ClientboundPackets1_18.SET_DISPLAY_OBJECTIVE, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.BYTE); // Slot\n                map(Types.STRING); // Name\n                handler(cutName(0, 16));\n            }\n        });\n        appendClientbound(ClientboundPackets1_18.SET_PLAYER_TEAM, cutName(0, 16));\n        registerClientbound(ClientboundPackets1_18.SET_SCORE, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.STRING); // Owner\n                map(Types.VAR_INT); // Method\n                map(Types.STRING); // Name\n                handler(cutName(0, 40));\n                handler(cutName(1, 16));\n            }\n        });\n    }\n\n    private PacketHandler cutName(final int index, final int maxLength) {\n        // May in some case cause clashes or bad ordering, but nothing we can do about that\n        return wrapper -> {\n            final String s = wrapper.get(Types.STRING, index);\n            if (s.length() > maxLength) {\n                wrapper.set(Types.STRING, index, s.substring(0, maxLength));\n            }\n        };\n    }\n\n    @Override\n    public void init(final UserConnection connection) {\n        addEntityTracker(connection, new EntityTrackerBase(connection, EntityTypes1_17.PLAYER));\n    }\n\n    @Override\n    public BackwardsMappingData1_18 getMappingData() {\n        return MAPPINGS;\n    }\n\n    @Override\n    public EntityPacketRewriter1_18 getEntityRewriter() {\n        return entityRewriter;\n    }\n\n    @Override\n    public BlockItemPacketRewriter1_18 getItemRewriter() {\n        return itemRewriter;\n    }\n\n    @Override\n    public ParticleRewriter<ClientboundPackets1_18> getParticleRewriter() {\n        return particleRewriter;\n    }\n\n    @Override\n    public JsonNBTComponentRewriter<ClientboundPackets1_18> getComponentRewriter() {\n        return translatableRewriter;\n    }\n\n    @Override\n    public TagRewriter<ClientboundPackets1_18> getTagRewriter() {\n        return tagRewriter;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_18to1_17_1/data/BackwardsMappingData1_18.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_18to1_17_1.data;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.viabackwards.api.data.BackwardsMappingData;\nimport com.viaversion.viaversion.libs.fastutil.ints.Int2ObjectMap;\nimport com.viaversion.viaversion.libs.fastutil.ints.Int2ObjectOpenHashMap;\nimport com.viaversion.viaversion.libs.fastutil.objects.Object2IntMap;\nimport com.viaversion.viaversion.protocols.v1_17_1to1_18.Protocol1_17_1To1_18;\nimport com.viaversion.viaversion.protocols.v1_17_1to1_18.data.BlockEntities1_18;\n\npublic final class BackwardsMappingData1_18 extends BackwardsMappingData {\n\n    private final Int2ObjectMap<String> blockEntities = new Int2ObjectOpenHashMap<>();\n\n    public BackwardsMappingData1_18() {\n        super(\"1.18\", \"1.17\", Protocol1_17_1To1_18.class);\n    }\n\n    @Override\n    protected void loadExtras(final CompoundTag data) {\n        super.loadExtras(data);\n\n        for (final Object2IntMap.Entry<String> entry : BlockEntities1_18.blockEntityIds().object2IntEntrySet()) {\n            blockEntities.put(entry.getIntValue(), entry.getKey());\n        }\n    }\n\n    public Int2ObjectMap<String> blockEntities() {\n        return blockEntities;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_18to1_17_1/data/BlockEntityMappings1_17_1.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_18to1_17_1.data;\n\nimport com.viaversion.viaversion.protocols.v1_17_1to1_18.data.BlockEntityMappings1_18;\nimport java.util.Arrays;\n\npublic final class BlockEntityMappings1_17_1 {\n\n    private static final int[] IDS;\n\n    static {\n        final int[] ids = BlockEntityMappings1_18.getIds();\n        IDS = new int[Arrays.stream(ids).max().getAsInt() + 1];\n        Arrays.fill(IDS, -1);\n        for (int i = 0; i < ids.length; i++) {\n            final int id = ids[i];\n            if (id != -1) {\n                IDS[id] = i;\n            }\n        }\n    }\n\n    public static int mappedId(final int id) {\n        if (id < 0 || id > IDS.length) {\n            return -1;\n        }\n        return IDS[id];\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_18to1_17_1/rewriter/BlockItemPacketRewriter1_18.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_18to1_17_1.rewriter;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.viabackwards.api.rewriters.BackwardsItemRewriter;\nimport com.viaversion.viabackwards.protocol.v1_18to1_17_1.Protocol1_18To1_17_1;\nimport com.viaversion.viabackwards.protocol.v1_18to1_17_1.data.BlockEntityMappings1_17_1;\nimport com.viaversion.viaversion.api.data.ParticleMappings;\nimport com.viaversion.viaversion.api.data.entity.EntityTracker;\nimport com.viaversion.viaversion.api.minecraft.BlockPosition;\nimport com.viaversion.viaversion.api.minecraft.blockentity.BlockEntity;\nimport com.viaversion.viaversion.api.minecraft.chunks.BaseChunk;\nimport com.viaversion.viaversion.api.minecraft.chunks.Chunk;\nimport com.viaversion.viaversion.api.minecraft.chunks.ChunkSection;\nimport com.viaversion.viaversion.api.minecraft.chunks.DataPalette;\nimport com.viaversion.viaversion.api.minecraft.chunks.PaletteType;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.chunk.ChunkType1_17;\nimport com.viaversion.viaversion.api.type.types.chunk.ChunkType1_18;\nimport com.viaversion.viaversion.protocols.v1_16_4to1_17.packet.ServerboundPackets1_17;\nimport com.viaversion.viaversion.protocols.v1_17_1to1_18.packet.ClientboundPackets1_18;\nimport com.viaversion.viaversion.protocols.v1_17to1_17_1.packet.ClientboundPackets1_17_1;\nimport com.viaversion.viaversion.rewriter.RecipeRewriter;\nimport com.viaversion.viaversion.util.Key;\nimport com.viaversion.viaversion.util.MathUtil;\nimport java.util.ArrayList;\nimport java.util.BitSet;\nimport java.util.List;\n\npublic final class BlockItemPacketRewriter1_18 extends BackwardsItemRewriter<ClientboundPackets1_18, ServerboundPackets1_17, Protocol1_18To1_17_1> {\n\n    public BlockItemPacketRewriter1_18(final Protocol1_18To1_17_1 protocol) {\n        super(protocol, Types.ITEM1_13_2, Types.ITEM1_13_2_ARRAY);\n    }\n\n    @Override\n    protected void registerPackets() {\n        new RecipeRewriter<>(protocol).register(ClientboundPackets1_18.UPDATE_RECIPES);\n\n        protocol.registerClientbound(ClientboundPackets1_18.LEVEL_EVENT, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.INT); // Effect id\n                map(Types.BLOCK_POSITION1_14); // Location\n                map(Types.INT); // Data\n                handler(wrapper -> {\n                    int id = wrapper.get(Types.INT, 0);\n                    int data = wrapper.get(Types.INT, 1);\n                    if (id == 1010) { // Play record\n                        wrapper.set(Types.INT, 1, protocol.getMappingData().getNewItemId(data));\n                    }\n                });\n            }\n        });\n\n        protocol.replaceClientbound(ClientboundPackets1_18.LEVEL_PARTICLES, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.INT); // Particle id\n                map(Types.BOOLEAN); // Override limiter\n                map(Types.DOUBLE); // X\n                map(Types.DOUBLE); // Y\n                map(Types.DOUBLE); // Z\n                map(Types.FLOAT); // Offset X\n                map(Types.FLOAT); // Offset Y\n                map(Types.FLOAT); // Offset Z\n                map(Types.FLOAT); // Max speed\n                map(Types.INT); // Particle Count\n                handler(wrapper -> {\n                    int id = wrapper.get(Types.INT, 0);\n                    if (id == 3) { // Block marker\n                        int blockState = wrapper.read(Types.VAR_INT);\n                        if (blockState == 7786) { // Light block\n                            wrapper.set(Types.INT, 0, 3);\n                        } else {\n                            // Else assume barrier block\n                            wrapper.set(Types.INT, 0, 2);\n                        }\n                        return;\n                    }\n\n                    ParticleMappings mappings = protocol.getMappingData().getParticleMappings();\n                    if (mappings.isBlockParticle(id)) {\n                        int data = wrapper.passthrough(Types.VAR_INT);\n                        wrapper.set(Types.VAR_INT, 0, protocol.getMappingData().getNewBlockStateId(data));\n                    } else if (mappings.isItemParticle(id)) {\n                        passthroughClientboundItem(wrapper);\n                    }\n\n                    int newId = protocol.getMappingData().getNewParticleId(id);\n                    if (newId != id) {\n                        wrapper.set(Types.INT, 0, newId);\n                    }\n                });\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_18.BLOCK_ENTITY_DATA, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.BLOCK_POSITION1_14);\n                handler(wrapper -> {\n                    final int id = wrapper.read(Types.VAR_INT);\n                    final CompoundTag tag = wrapper.read(Types.NAMED_COMPOUND_TAG);\n\n                    final int mappedId = BlockEntityMappings1_17_1.mappedId(id);\n                    if (mappedId == -1) {\n                        wrapper.cancel();\n                        return;\n                    }\n\n                    final String identifier = protocol.getMappingData().blockEntities().get(id);\n                    if (identifier == null) {\n                        wrapper.cancel();\n                        return;\n                    }\n\n                    // The 1.18 server doesn't include the id and positions (x, y, z) in the NBT anymore\n                    // If those were the only fields on the block entity (e.g.: skull, bed), we'll receive a null NBT\n                    // We initialize one and add the missing fields, so it can be handled correctly down the line\n                    final CompoundTag newTag = tag == null ? new CompoundTag() : tag;\n                    final BlockPosition pos = wrapper.get(Types.BLOCK_POSITION1_14, 0);\n\n                    // The protocol converters downstream rely on this field, let's add it back\n                    newTag.putString(\"id\", Key.namespaced(identifier));\n\n                    // Weird glitches happen with the 1.17 client and below if these fields are missing\n                    // Some examples are block entity models becoming invisible (e.g.: signs, banners)\n                    newTag.putInt(\"x\", pos.x());\n                    newTag.putInt(\"y\", pos.y());\n                    newTag.putInt(\"z\", pos.z());\n\n                    handleSpawner(id, newTag);\n                    wrapper.write(Types.UNSIGNED_BYTE, (short) mappedId);\n                    wrapper.write(Types.NAMED_COMPOUND_TAG, newTag);\n                });\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_18.LEVEL_CHUNK_WITH_LIGHT, ClientboundPackets1_17_1.LEVEL_CHUNK, wrapper -> {\n            final EntityTracker tracker = protocol.getEntityRewriter().tracker(wrapper.user());\n            final ChunkType1_18 chunkType = new ChunkType1_18(tracker.currentWorldSectionHeight(),\n                MathUtil.ceilLog2(protocol.getMappingData().getBlockStateMappings().mappedSize()),\n                MathUtil.ceilLog2(tracker.biomesSent()));\n            final Chunk oldChunk = wrapper.read(chunkType);\n            final ChunkSection[] sections = oldChunk.getSections();\n            final BitSet mask = new BitSet(oldChunk.getSections().length);\n            final int[] biomeData = new int[sections.length * ChunkSection.BIOME_SIZE];\n            int biomeIndex = 0;\n            for (int j = 0; j < sections.length; j++) {\n                final ChunkSection section = sections[j];\n                // Write biome palette into biome array\n                final DataPalette biomePalette = section.palette(PaletteType.BIOMES);\n                for (int i = 0; i < ChunkSection.BIOME_SIZE; i++) {\n                    biomeData[biomeIndex++] = biomePalette.idAt(i);\n                }\n\n                // Rewrite to empty section\n                if (section.getNonAirBlocksCount() == 0) {\n                    sections[j] = null;\n                } else {\n                    mask.set(j);\n                }\n            }\n\n            final List<CompoundTag> blockEntityTags = new ArrayList<>(oldChunk.blockEntities().size());\n            for (final BlockEntity blockEntity : oldChunk.blockEntities()) {\n                final String id = protocol.getMappingData().blockEntities().get(blockEntity.typeId());\n                if (id == null) {\n                    // Shrug\n                    continue;\n                }\n\n                final CompoundTag tag;\n                if (blockEntity.tag() != null) {\n                    tag = blockEntity.tag();\n                    handleSpawner(blockEntity.typeId(), tag);\n                } else {\n                    tag = new CompoundTag();\n                }\n\n                blockEntityTags.add(tag);\n                tag.putInt(\"x\", (oldChunk.getX() << 4) + blockEntity.sectionX());\n                tag.putInt(\"y\", blockEntity.y());\n                tag.putInt(\"z\", (oldChunk.getZ() << 4) + blockEntity.sectionZ());\n                tag.putString(\"id\", Key.namespaced(id));\n            }\n\n            final Chunk chunk = new BaseChunk(oldChunk.getX(), oldChunk.getZ(), true, false, mask,\n                oldChunk.getSections(), biomeData, oldChunk.getHeightMap(), blockEntityTags);\n            wrapper.write(new ChunkType1_17(tracker.currentWorldSectionHeight()), chunk);\n\n            // Create and send light packet first\n            final PacketWrapper lightPacket = wrapper.create(ClientboundPackets1_17_1.LIGHT_UPDATE);\n            lightPacket.write(Types.VAR_INT, chunk.getX());\n            lightPacket.write(Types.VAR_INT, chunk.getZ());\n            lightPacket.write(Types.BOOLEAN, wrapper.read(Types.BOOLEAN)); // Trust edges\n            lightPacket.write(Types.LONG_ARRAY_PRIMITIVE, wrapper.read(Types.LONG_ARRAY_PRIMITIVE)); // Sky light mask\n            lightPacket.write(Types.LONG_ARRAY_PRIMITIVE, wrapper.read(Types.LONG_ARRAY_PRIMITIVE)); // Block light mask\n            lightPacket.write(Types.LONG_ARRAY_PRIMITIVE, wrapper.read(Types.LONG_ARRAY_PRIMITIVE)); // Empty sky light mask\n            lightPacket.write(Types.LONG_ARRAY_PRIMITIVE, wrapper.read(Types.LONG_ARRAY_PRIMITIVE)); // Empty block light mask\n\n            final int skyLightLength = wrapper.read(Types.VAR_INT);\n            lightPacket.write(Types.VAR_INT, skyLightLength);\n            for (int i = 0; i < skyLightLength; i++) {\n                lightPacket.write(Types.BYTE_ARRAY_PRIMITIVE, wrapper.read(Types.BYTE_ARRAY_PRIMITIVE));\n            }\n\n            final int blockLightLength = wrapper.read(Types.VAR_INT);\n            lightPacket.write(Types.VAR_INT, blockLightLength);\n            for (int i = 0; i < blockLightLength; i++) {\n                lightPacket.write(Types.BYTE_ARRAY_PRIMITIVE, wrapper.read(Types.BYTE_ARRAY_PRIMITIVE));\n            }\n\n            lightPacket.send(Protocol1_18To1_17_1.class);\n        });\n\n        protocol.cancelClientbound(ClientboundPackets1_18.SET_SIMULATION_DISTANCE);\n    }\n\n    private void handleSpawner(final int typeId, final CompoundTag tag) {\n        if (typeId == 8) {\n            final CompoundTag spawnData = tag.getCompoundTag(\"SpawnData\");\n            final CompoundTag entity;\n            if (spawnData != null && (entity = spawnData.getCompoundTag(\"entity\")) != null) {\n                tag.put(\"SpawnData\", entity);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_18to1_17_1/rewriter/EntityPacketRewriter1_18.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_18to1_17_1.rewriter;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.ListTag;\nimport com.viaversion.nbt.tag.StringTag;\nimport com.viaversion.viabackwards.api.rewriters.EntityRewriter;\nimport com.viaversion.viabackwards.protocol.v1_18to1_17_1.Protocol1_18To1_17_1;\nimport com.viaversion.viaversion.api.minecraft.Particle;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityType;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_17;\nimport com.viaversion.viaversion.api.minecraft.entitydata.EntityDataType;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.version.Types1_17;\nimport com.viaversion.viaversion.api.type.types.version.Types1_18;\nimport com.viaversion.viaversion.protocols.v1_17_1to1_18.packet.ClientboundPackets1_18;\nimport com.viaversion.viaversion.util.TagUtil;\n\npublic final class EntityPacketRewriter1_18 extends EntityRewriter<ClientboundPackets1_18, Protocol1_18To1_17_1> {\n\n    public EntityPacketRewriter1_18(final Protocol1_18To1_17_1 protocol) {\n        super(protocol);\n    }\n\n    @Override\n    protected void registerPackets() {\n        registerSetEntityData(ClientboundPackets1_18.SET_ENTITY_DATA, Types1_18.ENTITY_DATA_LIST, Types1_17.ENTITY_DATA_LIST);\n\n        protocol.registerClientbound(ClientboundPackets1_18.LOGIN, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.INT); // Entity ID\n                map(Types.BOOLEAN); // Hardcore\n                map(Types.BYTE); // Gamemode\n                map(Types.BYTE); // Previous Gamemode\n                map(Types.STRING_ARRAY); // Worlds\n                map(Types.NAMED_COMPOUND_TAG); // Dimension registry\n                map(Types.NAMED_COMPOUND_TAG); // Current dimension data\n                map(Types.STRING); // World\n                map(Types.LONG); // Seed\n                map(Types.VAR_INT); // Max players\n                map(Types.VAR_INT); // Chunk radius\n                read(Types.VAR_INT); // Read simulation distance\n                handler(worldDataTrackerHandler(1));\n                handler(wrapper -> {\n                    final CompoundTag registry = wrapper.get(Types.NAMED_COMPOUND_TAG, 0);\n                    final ListTag<CompoundTag> biomes = TagUtil.getRegistryEntries(registry, \"worldgen/biome\");\n                    for (final CompoundTag biome : biomes) {\n                        final CompoundTag biomeCompound = biome.getCompoundTag(\"element\");\n                        final StringTag category = biomeCompound.getStringTag(\"category\");\n                        if (category.getValue().equals(\"mountain\")) {\n                            category.setValue(\"extreme_hills\");\n                        }\n\n                        // The client just needs something\n                        biomeCompound.putFloat(\"depth\", 0.125F);\n                        biomeCompound.putFloat(\"scale\", 0.05F);\n                    }\n\n                    // Track amount of biomes sent\n                    tracker(wrapper.user()).setBiomesSent(biomes.size());\n                });\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_18.RESPAWN, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.NAMED_COMPOUND_TAG); // Dimension data\n                map(Types.STRING); // World\n                handler(worldDataTrackerHandler(0));\n            }\n        });\n    }\n\n    @Override\n    protected void registerRewrites() {\n        filter().handler((event, data) -> {\n            data.setDataType(Types1_17.ENTITY_DATA_TYPES.byId(data.dataType().typeId()));\n\n            EntityDataType type = data.dataType();\n            if (type == Types1_17.ENTITY_DATA_TYPES.particleType) {\n                Particle particle = data.value();\n                if (particle.id() == 3) { // Block marker\n                    Particle.ParticleData<?> value = particle.getArguments().remove(0);\n                    int blockState = (int) value.getValue();\n                    if (blockState == 7786) { // Light block\n                        particle.setId(3);\n                    } else {\n                        // Else assume barrier block\n                        particle.setId(2);\n                    }\n                    return;\n                }\n\n                protocol.getParticleRewriter().rewriteParticle(event.user(), particle);\n            }\n        });\n\n        // Particles have already been handled\n        registerEntityDataTypeHandler(Types1_17.ENTITY_DATA_TYPES.itemType, null, null, null,\n            Types1_17.ENTITY_DATA_TYPES.componentType, Types1_17.ENTITY_DATA_TYPES.optionalComponentType);\n    }\n\n    @Override\n    public EntityType typeFromId(final int typeId) {\n        return EntityTypes1_17.getTypeFromId(typeId);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_19_1to1_19/Protocol1_19_1To1_19.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_19_1to1_19;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.ListTag;\nimport com.viaversion.nbt.tag.NumberTag;\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viabackwards.api.rewriters.text.JsonNBTComponentRewriter;\nimport com.viaversion.viabackwards.protocol.v1_19_1to1_19.rewriter.EntityPacketRewriter1_19_1;\nimport com.viaversion.viabackwards.protocol.v1_19_1to1_19.storage.ChatRegistryStorage;\nimport com.viaversion.viabackwards.protocol.v1_19_1to1_19.storage.ChatRegistryStorage1_19_1;\nimport com.viaversion.viabackwards.protocol.v1_19_1to1_19.storage.NonceStorage;\nimport com.viaversion.viabackwards.protocol.v1_19_1to1_19.storage.ReceivedMessagesStorage;\nimport com.viaversion.viaversion.api.Via;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.PlayerMessageSignature;\nimport com.viaversion.viaversion.api.minecraft.ProfileKey;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_19;\nimport com.viaversion.viaversion.api.minecraft.signature.SignableCommandArgumentsProvider;\nimport com.viaversion.viaversion.api.minecraft.signature.model.DecoratableMessage;\nimport com.viaversion.viaversion.api.minecraft.signature.model.MessageMetadata;\nimport com.viaversion.viaversion.api.minecraft.signature.storage.ChatSession1_19_1;\nimport com.viaversion.viaversion.api.protocol.Protocol;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.protocol.packet.State;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.data.entity.EntityTrackerBase;\nimport com.viaversion.viaversion.libs.gson.JsonElement;\nimport com.viaversion.viaversion.protocols.base.ClientboundLoginPackets;\nimport com.viaversion.viaversion.protocols.base.ServerboundLoginPackets;\nimport com.viaversion.viaversion.protocols.v1_18_2to1_19.Protocol1_18_2To1_19;\nimport com.viaversion.viaversion.protocols.v1_18_2to1_19.packet.ClientboundPackets1_19;\nimport com.viaversion.viaversion.protocols.v1_18_2to1_19.packet.ServerboundPackets1_19;\nimport com.viaversion.viaversion.protocols.v1_19to1_19_1.Protocol1_19To1_19_1;\nimport com.viaversion.viaversion.protocols.v1_19to1_19_1.packet.ClientboundPackets1_19_1;\nimport com.viaversion.viaversion.protocols.v1_19to1_19_1.packet.ServerboundPackets1_19_1;\nimport com.viaversion.viaversion.rewriter.text.ComponentRewriterBase;\nimport com.viaversion.viaversion.util.CipherUtil;\nimport com.viaversion.viaversion.util.ComponentUtil;\nimport com.viaversion.viaversion.util.Pair;\nimport com.viaversion.viaversion.util.TagUtil;\nimport java.security.SignatureException;\nimport java.util.List;\nimport java.util.UUID;\nimport org.checkerframework.checker.nullness.qual.Nullable;\n\npublic final class Protocol1_19_1To1_19 extends BackwardsProtocol<ClientboundPackets1_19_1, ClientboundPackets1_19, ServerboundPackets1_19_1, ServerboundPackets1_19> {\n\n    public static final int SYSTEM_CHAT_ID = 1;\n    public static final int GAME_INFO_ID = 2;\n    private static final UUID ZERO_UUID = new UUID(0, 0);\n    private static final byte[] EMPTY_BYTES = new byte[0];\n    private final EntityPacketRewriter1_19_1 entityRewriter = new EntityPacketRewriter1_19_1(this);\n    private final JsonNBTComponentRewriter<ClientboundPackets1_19_1> translatableRewriter = new JsonNBTComponentRewriter<>(this, ComponentRewriterBase.ReadType.JSON);\n\n    public Protocol1_19_1To1_19() {\n        super(ClientboundPackets1_19_1.class, ClientboundPackets1_19.class, ServerboundPackets1_19_1.class, ServerboundPackets1_19.class);\n    }\n\n    @Override\n    protected void registerPackets() {\n        super.registerPackets();\n\n        registerClientbound(ClientboundPackets1_19_1.LOGIN, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.INT); // Entity ID\n                map(Types.BOOLEAN); // Hardcore\n                map(Types.BYTE); // Gamemode\n                map(Types.BYTE); // Previous Gamemode\n                map(Types.STRING_ARRAY); // World List\n                map(Types.NAMED_COMPOUND_TAG); // Dimension registry\n                map(Types.STRING); // Dimension key\n                map(Types.STRING); // World\n                handler(wrapper -> {\n                    final ChatRegistryStorage chatTypeStorage = wrapper.user().get(ChatRegistryStorage1_19_1.class);\n                    chatTypeStorage.clear();\n\n                    final CompoundTag registry = wrapper.get(Types.NAMED_COMPOUND_TAG, 0);\n                    final ListTag<CompoundTag> chatTypes = TagUtil.removeRegistryEntries(registry, \"chat_type\", new ListTag<>(CompoundTag.class));\n                    for (final CompoundTag chatType : chatTypes) {\n                        final NumberTag idTag = chatType.getNumberTag(\"id\");\n                        chatTypeStorage.addChatType(idTag.asInt(), chatType);\n                    }\n\n                    // Replace with 1.19 chat types\n                    // Ensures that the client has a chat type for system message, with and without overlay\n                    registry.put(\"minecraft:chat_type\", Protocol1_18_2To1_19.MAPPINGS.chatRegistry());\n                });\n                handler(entityRewriter.worldTrackerHandlerByKey());\n            }\n        });\n\n        registerClientbound(ClientboundPackets1_19_1.PLAYER_CHAT, ClientboundPackets1_19.SYSTEM_CHAT, wrapper -> {\n            wrapper.read(Types.OPTIONAL_BYTE_ARRAY_PRIMITIVE); // Previous signature\n\n            final PlayerMessageSignature signature = wrapper.read(Types.PLAYER_MESSAGE_SIGNATURE);\n\n            // Store message signature for last seen\n            if (!signature.uuid().equals(ZERO_UUID) && signature.signatureBytes().length != 0) {\n                final ReceivedMessagesStorage messagesStorage = wrapper.user().get(ReceivedMessagesStorage.class);\n                messagesStorage.add(signature);\n                if (messagesStorage.tickUnacknowledged() > 64) {\n                    messagesStorage.resetUnacknowledgedCount();\n\n                    // Send chat acknowledgement\n                    final PacketWrapper chatAckPacket = wrapper.create(ServerboundPackets1_19_1.CHAT_ACK);\n                    chatAckPacket.write(Types.PLAYER_MESSAGE_SIGNATURE_ARRAY, messagesStorage.lastSignatures());\n                    chatAckPacket.write(Types.OPTIONAL_PLAYER_MESSAGE_SIGNATURE, null);\n                    chatAckPacket.sendToServer(Protocol1_19_1To1_19.class);\n                }\n            }\n\n            // Send the unsigned message if present, otherwise the signed message\n            final String plainMessage = wrapper.read(Types.STRING); // Plain message\n            JsonElement message = null;\n            JsonElement decoratedMessage = wrapper.read(Types.OPTIONAL_COMPONENT);\n            if (decoratedMessage != null) {\n                message = decoratedMessage;\n            }\n\n            wrapper.read(Types.LONG); // Timestamp\n            wrapper.read(Types.LONG); // Salt\n            wrapper.read(Types.PLAYER_MESSAGE_SIGNATURE_ARRAY); // Last seen\n\n            final JsonElement unsignedMessage = wrapper.read(Types.OPTIONAL_COMPONENT);\n            if (unsignedMessage != null) {\n                message = unsignedMessage;\n            }\n            if (message == null) {\n                // If no decorated or unsigned message is given, use the plain one\n                message = ComponentUtil.plainToJson(plainMessage);\n            }\n\n            final int filterMaskType = wrapper.read(Types.VAR_INT);\n            if (filterMaskType == 2) { // Partially filtered\n                wrapper.read(Types.LONG_ARRAY_PRIMITIVE); // Mask\n            }\n\n            final int chatTypeId = wrapper.read(Types.VAR_INT);\n            final JsonElement senderName = wrapper.read(Types.COMPONENT);\n            final JsonElement targetName = wrapper.read(Types.OPTIONAL_COMPONENT);\n            decoratedMessage = decorateChatMessage(this, wrapper.user().get(ChatRegistryStorage1_19_1.class), chatTypeId, senderName, targetName, message);\n            if (decoratedMessage == null) {\n                wrapper.cancel();\n                return;\n            }\n\n            translatableRewriter.processText(wrapper.user(), decoratedMessage);\n            wrapper.write(Types.COMPONENT, decoratedMessage);\n            wrapper.write(Types.VAR_INT, SYSTEM_CHAT_ID);\n        });\n\n        replaceClientbound(ClientboundPackets1_19_1.SYSTEM_CHAT, wrapper -> {\n            final JsonElement content = wrapper.passthrough(Types.COMPONENT);\n            translatableRewriter.processText(wrapper.user(), content);\n\n            final boolean overlay = wrapper.read(Types.BOOLEAN);\n            wrapper.write(Types.VAR_INT, overlay ? GAME_INFO_ID : SYSTEM_CHAT_ID);\n        });\n\n        registerServerbound(ServerboundPackets1_19.CHAT, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.STRING); // Message\n                map(Types.LONG); // Timestamp\n                map(Types.LONG); // Salt\n                read(Types.BYTE_ARRAY_PRIMITIVE); // Signature\n                read(Types.BOOLEAN); // Signed preview\n                handler(wrapper -> {\n                    final ChatSession1_19_1 chatSession = wrapper.user().get(ChatSession1_19_1.class);\n                    final ReceivedMessagesStorage messagesStorage = wrapper.user().get(ReceivedMessagesStorage.class);\n\n                    if (chatSession != null) {\n                        final UUID sender = wrapper.user().getProtocolInfo().getUuid();\n                        final String message = wrapper.get(Types.STRING, 0);\n                        final long timestamp = wrapper.get(Types.LONG, 0);\n                        final long salt = wrapper.get(Types.LONG, 1);\n\n                        final MessageMetadata metadata = new MessageMetadata(sender, timestamp, salt);\n                        final DecoratableMessage decoratableMessage = new DecoratableMessage(message);\n                        final byte[] signature;\n                        try {\n                            signature = chatSession.signChatMessage(metadata, decoratableMessage, messagesStorage.lastSignatures());\n                        } catch (final SignatureException e) {\n                            throw new RuntimeException(e);\n                        }\n\n                        wrapper.write(Types.BYTE_ARRAY_PRIMITIVE, signature); // Signature\n                        wrapper.write(Types.BOOLEAN, decoratableMessage.isDecorated()); // Signed preview\n                    } else {\n                        wrapper.write(Types.BYTE_ARRAY_PRIMITIVE, EMPTY_BYTES); // Signature\n                        wrapper.write(Types.BOOLEAN, false); // Signed preview\n                    }\n\n                    messagesStorage.resetUnacknowledgedCount();\n                    wrapper.write(Types.PLAYER_MESSAGE_SIGNATURE_ARRAY, messagesStorage.lastSignatures());\n                    wrapper.write(Types.OPTIONAL_PLAYER_MESSAGE_SIGNATURE, null); // No last unacknowledged\n                });\n            }\n        });\n\n        registerServerbound(ServerboundPackets1_19.CHAT_COMMAND, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.STRING); // Command\n                map(Types.LONG); // Timestamp\n                map(Types.LONG); // Salt\n                handler(wrapper -> {\n                    final ReceivedMessagesStorage messagesStorage = wrapper.user().get(ReceivedMessagesStorage.class);\n                    final ChatSession1_19_1 chatSession = wrapper.user().get(ChatSession1_19_1.class);\n                    final SignableCommandArgumentsProvider argumentsProvider = Via.getManager().getProviders().get(SignableCommandArgumentsProvider.class);\n\n                    if (chatSession != null && argumentsProvider != null) {\n                        final int signatures = wrapper.read(Types.VAR_INT);\n                        for (int i = 0; i < signatures; i++) {\n                            wrapper.read(Types.STRING); // Argument name\n                            wrapper.read(Types.BYTE_ARRAY_PRIMITIVE); // Signature\n                        }\n\n                        final UUID sender = wrapper.user().getProtocolInfo().getUuid();\n                        final String command = wrapper.get(Types.STRING, 0);\n                        final long timestamp = wrapper.get(Types.LONG, 0);\n                        final long salt = wrapper.get(Types.LONG, 1);\n\n                        final MessageMetadata metadata = new MessageMetadata(sender, timestamp, salt);\n\n                        final List<Pair<String, String>> arguments = argumentsProvider.getSignableArguments(command);\n                        wrapper.write(Types.VAR_INT, arguments.size());\n                        for (final Pair<String, String> argument : arguments) {\n                            final byte[] signature;\n                            try {\n                                signature = chatSession.signChatMessage(metadata, new DecoratableMessage(argument.value()), messagesStorage.lastSignatures());\n                            } catch (final SignatureException e) {\n                                throw new RuntimeException(e);\n                            }\n\n                            wrapper.write(Types.STRING, argument.key());\n                            wrapper.write(Types.BYTE_ARRAY_PRIMITIVE, signature);\n                        }\n                    } else {\n                        final int signatures = wrapper.passthrough(Types.VAR_INT);\n                        for (int i = 0; i < signatures; i++) {\n                            wrapper.passthrough(Types.STRING); // Argument name\n\n                            // Set empty signature\n                            wrapper.read(Types.BYTE_ARRAY_PRIMITIVE);\n                            wrapper.write(Types.BYTE_ARRAY_PRIMITIVE, EMPTY_BYTES);\n                        }\n                    }\n\n                    wrapper.passthrough(Types.BOOLEAN); // Signed preview\n\n                    messagesStorage.resetUnacknowledgedCount();\n                    wrapper.write(Types.PLAYER_MESSAGE_SIGNATURE_ARRAY, messagesStorage.lastSignatures());\n                    wrapper.write(Types.OPTIONAL_PLAYER_MESSAGE_SIGNATURE, null); // No last unacknowledged\n                });\n            }\n        });\n\n        replaceClientbound(ClientboundPackets1_19_1.SERVER_DATA, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.OPTIONAL_COMPONENT); // Motd\n                map(Types.OPTIONAL_STRING); // Encoded icon\n                map(Types.BOOLEAN); // Previews chat\n                read(Types.BOOLEAN); // Enforces secure chat\n            }\n        });\n\n        registerServerbound(State.LOGIN, ServerboundLoginPackets.HELLO, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.STRING); // Name\n                handler(wrapper -> {\n                    final ProfileKey profileKey = wrapper.read(Types.OPTIONAL_PROFILE_KEY); // Profile Key\n\n                    final ChatSession1_19_1 chatSession = wrapper.user().get(ChatSession1_19_1.class);\n                    wrapper.write(Types.OPTIONAL_PROFILE_KEY, chatSession == null ? null : chatSession.getProfileKey()); // Profile Key\n                    wrapper.write(Types.OPTIONAL_UUID, chatSession == null ? null : chatSession.getUuid()); // Profile uuid\n\n                    if (profileKey == null || chatSession != null) {\n                        wrapper.user().put(new NonceStorage(null));\n                    }\n                });\n            }\n        });\n\n        registerClientbound(State.LOGIN, ClientboundLoginPackets.HELLO, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.STRING); // Server id\n                handler(wrapper -> {\n                    if (wrapper.user().has(NonceStorage.class)) {\n                        return;\n                    }\n\n                    final byte[] publicKey = wrapper.passthrough(Types.BYTE_ARRAY_PRIMITIVE);\n                    final byte[] nonce = wrapper.passthrough(Types.BYTE_ARRAY_PRIMITIVE);\n                    wrapper.user().put(new NonceStorage(CipherUtil.encryptNonce(publicKey, nonce)));\n                });\n            }\n        });\n\n        registerServerbound(State.LOGIN, ServerboundLoginPackets.ENCRYPTION_KEY, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.BYTE_ARRAY_PRIMITIVE); // Key\n                handler(wrapper -> {\n                    final NonceStorage nonceStorage = wrapper.user().remove(NonceStorage.class);\n                    if (nonceStorage.nonce() == null) {\n                        return;\n                    }\n\n                    final boolean isNonce = wrapper.read(Types.BOOLEAN);\n                    wrapper.write(Types.BOOLEAN, true);\n                    if (!isNonce) { // Should never be true at this point, but /shrug otherwise\n                        wrapper.read(Types.LONG); // Salt\n                        wrapper.read(Types.BYTE_ARRAY_PRIMITIVE); // Signature\n                        wrapper.write(Types.BYTE_ARRAY_PRIMITIVE, nonceStorage.nonce());\n                    }\n                });\n            }\n        });\n\n        registerClientbound(State.LOGIN, ClientboundLoginPackets.CUSTOM_QUERY, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT);\n                map(Types.STRING);\n                handler(wrapper -> {\n                    final String identifier = wrapper.get(Types.STRING, 0);\n                    if (identifier.equals(\"velocity:player_info\")) {\n                        byte[] data = wrapper.passthrough(Types.REMAINING_BYTES);\n                        // Velocity modern forwarding version above 1 includes the players public key.\n                        // This is an issue because the player does not have a public key.\n                        // Velocity itself does adjust the version accordingly: https://github.com/PaperMC/Velocity/blob/1a3fba4250553702d9dcd05731d04347bfc24c9f/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/LoginSessionHandler.java#L176-L197\n                        // However this becomes an issue when an 1.19.0 client tries to join a 1.19.1 server.\n                        // (The protocol translation will go 1.19.1 -> 1.18.2 -> 1.19.0)\n                        // The player does have a public key, but an outdated one.\n                        // Velocity modern forwarding versions: https://github.com/PaperMC/Velocity/blob/1a3fba4250553702d9dcd05731d04347bfc24c9f/proxy/src/main/java/com/velocitypowered/proxy/connection/VelocityConstants.java#L27-L29\n                        // And the version can be specified with a single byte: https://github.com/PaperMC/Velocity/blob/1a3fba4250553702d9dcd05731d04347bfc24c9f/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/LoginSessionHandler.java#L88\n                        if (data.length == 1 && data[0] > 1) {\n                            data[0] = 1;\n                        } else if (data.length == 0) { // Or the version is omitted (default version would be used)\n                            data = new byte[]{1};\n                            wrapper.set(Types.REMAINING_BYTES, 0, data);\n                        } else {\n                            getLogger().warning(\"Received unexpected data in velocity:player_info (length=\" + data.length + \")\");\n                        }\n                    }\n                });\n            }\n        });\n\n        cancelClientbound(ClientboundPackets1_19_1.CUSTOM_CHAT_COMPLETIONS); // Can't do anything with them unless we add clutter clients with fake player profiles\n        cancelClientbound(ClientboundPackets1_19_1.DELETE_CHAT); // Can't do without the old \"send 50 empty lines and then resend previous messages\" trick\n        cancelClientbound(ClientboundPackets1_19_1.PLAYER_CHAT_HEADER);\n    }\n\n    @Override\n    public void init(final UserConnection user) {\n        user.put(new ChatRegistryStorage1_19_1());\n        user.put(new ReceivedMessagesStorage());\n        addEntityTracker(user, new EntityTrackerBase(user, EntityTypes1_19.PLAYER));\n    }\n\n    @Override\n    public JsonNBTComponentRewriter<ClientboundPackets1_19_1> getComponentRewriter() {\n        return translatableRewriter;\n    }\n\n    @Override\n    public EntityPacketRewriter1_19_1 getEntityRewriter() {\n        return entityRewriter;\n    }\n\n    public static @Nullable JsonElement decorateChatMessage(final Protocol protocol, final ChatRegistryStorage chatRegistryStorage,\n                                                            final int chatTypeId, final JsonElement senderName,\n                                                            @Nullable final JsonElement targetName, final JsonElement message) {\n        CompoundTag chatType = chatRegistryStorage.chatType(chatTypeId);\n        if (chatType == null) {\n            protocol.getLogger().warning(\"Chat message has unknown chat type id \" + chatTypeId + \". Message: \" + message);\n            return null;\n        }\n\n        chatType = chatType.getCompoundTag(\"element\").getCompoundTag(\"chat\");\n        if (chatType == null) {\n            return null;\n        }\n\n        return Protocol1_19To1_19_1.translatabaleComponentFromTag(chatType, senderName, targetName, message);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_19_1to1_19/rewriter/EntityPacketRewriter1_19_1.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_19_1to1_19.rewriter;\n\nimport com.viaversion.viabackwards.api.rewriters.EntityRewriter;\nimport com.viaversion.viabackwards.protocol.v1_19_1to1_19.Protocol1_19_1To1_19;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityType;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_19;\nimport com.viaversion.viaversion.api.type.types.version.Types1_19;\nimport com.viaversion.viaversion.protocols.v1_19to1_19_1.packet.ClientboundPackets1_19_1;\n\npublic final class EntityPacketRewriter1_19_1 extends EntityRewriter<ClientboundPackets1_19_1, Protocol1_19_1To1_19> {\n\n    public EntityPacketRewriter1_19_1(final Protocol1_19_1To1_19 protocol) {\n        super(protocol, Types1_19.ENTITY_DATA_TYPES.optionalComponentType, Types1_19.ENTITY_DATA_TYPES.booleanType);\n    }\n\n    @Override\n    protected void registerPackets() {\n        registerSetEntityData(ClientboundPackets1_19_1.SET_ENTITY_DATA, Types1_19.ENTITY_DATA_LIST);\n    }\n\n    @Override\n    public void registerRewrites() {\n        filter().type(EntityTypes1_19.ALLAY).cancel(16); // Dancing\n        filter().type(EntityTypes1_19.ALLAY).cancel(17); // Can duplicate\n    }\n\n    @Override\n    public EntityType typeFromId(final int typeId) {\n        return EntityTypes1_19.getTypeFromId(typeId);\n    }\n\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_19_1to1_19/storage/ChatRegistryStorage.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_19_1to1_19.storage;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.viaversion.api.connection.StorableObject;\nimport com.viaversion.viaversion.libs.fastutil.ints.Int2ObjectMap;\nimport com.viaversion.viaversion.libs.fastutil.ints.Int2ObjectOpenHashMap;\nimport com.viaversion.viaversion.protocols.v1_18_2to1_19.Protocol1_18_2To1_19;\nimport org.checkerframework.checker.nullness.qual.Nullable;\n\npublic abstract class ChatRegistryStorage implements StorableObject {\n\n    private final Int2ObjectMap<CompoundTag> chatTypes = new Int2ObjectOpenHashMap<>();\n\n    public @Nullable CompoundTag chatType(final int id) {\n        return chatTypes.isEmpty() ? Protocol1_18_2To1_19.MAPPINGS.chatType(id) : chatTypes.get(id);\n    }\n\n    public void addChatType(final int id, final CompoundTag chatType) {\n        chatTypes.put(id, chatType);\n    }\n\n    public void clear() {\n        chatTypes.clear();\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_19_1to1_19/storage/ChatRegistryStorage1_19_1.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_19_1to1_19.storage;\n\npublic final class ChatRegistryStorage1_19_1 extends ChatRegistryStorage {\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_19_1to1_19/storage/NonceStorage.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_19_1to1_19.storage;\n\nimport com.viaversion.viaversion.api.connection.StorableObject;\n\npublic record NonceStorage(byte[] nonce) implements StorableObject {\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_19_1to1_19/storage/ReceivedMessagesStorage.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_19_1to1_19.storage;\n\nimport com.viaversion.viaversion.api.connection.StorableObject;\nimport com.viaversion.viaversion.api.minecraft.PlayerMessageSignature;\nimport java.util.Arrays;\n\npublic final class ReceivedMessagesStorage implements StorableObject {\n    private final PlayerMessageSignature[] signatures = new PlayerMessageSignature[5];\n    private int size;\n    private int unacknowledged;\n\n    public void add(final PlayerMessageSignature signature) {\n        PlayerMessageSignature toPush = signature;\n        for (int i = 0; i < this.size; ++i) {\n            final PlayerMessageSignature entry = this.signatures[i];\n            this.signatures[i] = toPush;\n            toPush = entry;\n            if (entry.uuid().equals(signature.uuid())) {\n                return;\n            }\n        }\n\n        if (this.size < this.signatures.length) {\n            this.signatures[this.size++] = toPush;\n        }\n    }\n\n    public PlayerMessageSignature[] lastSignatures() {\n        return Arrays.copyOf(this.signatures, size);\n    }\n\n    public int tickUnacknowledged() {\n        return unacknowledged++;\n    }\n\n    public void resetUnacknowledgedCount() {\n        unacknowledged = 0;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_19_3to1_19_1/Protocol1_19_3To1_19_1.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_19_3to1_19_1;\n\nimport com.google.common.base.Preconditions;\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viabackwards.api.data.BackwardsMappingData;\nimport com.viaversion.viabackwards.api.rewriters.text.JsonNBTComponentRewriter;\nimport com.viaversion.viabackwards.protocol.v1_19_1to1_19.Protocol1_19_1To1_19;\nimport com.viaversion.viabackwards.protocol.v1_19_3to1_19_1.rewriter.BlockItemPacketRewriter1_19_3;\nimport com.viaversion.viabackwards.protocol.v1_19_3to1_19_1.rewriter.EntityPacketRewriter1_19_3;\nimport com.viaversion.viabackwards.protocol.v1_19_3to1_19_1.storage.ChatSessionStorage;\nimport com.viaversion.viabackwards.protocol.v1_19_3to1_19_1.storage.ChatTypeStorage1_19_3;\nimport com.viaversion.viabackwards.protocol.v1_19_3to1_19_1.storage.NonceStorage;\nimport com.viaversion.viaversion.api.Via;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.Holder;\nimport com.viaversion.viaversion.api.minecraft.PlayerMessageSignature;\nimport com.viaversion.viaversion.api.minecraft.ProfileKey;\nimport com.viaversion.viaversion.api.minecraft.RegistryType;\nimport com.viaversion.viaversion.api.minecraft.SoundEvent;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_19_3;\nimport com.viaversion.viaversion.api.minecraft.signature.SignableCommandArgumentsProvider;\nimport com.viaversion.viaversion.api.minecraft.signature.model.MessageMetadata;\nimport com.viaversion.viaversion.api.minecraft.signature.storage.ChatSession1_19_3;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.protocol.packet.State;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.BitSetType;\nimport com.viaversion.viaversion.api.type.types.ByteArrayType;\nimport com.viaversion.viaversion.api.type.types.chunk.ChunkType1_18;\nimport com.viaversion.viaversion.data.entity.EntityTrackerBase;\nimport com.viaversion.viaversion.libs.gson.JsonElement;\nimport com.viaversion.viaversion.protocols.base.ClientboundLoginPackets;\nimport com.viaversion.viaversion.protocols.base.ServerboundLoginPackets;\nimport com.viaversion.viaversion.protocols.v1_19_1to1_19_3.Protocol1_19_1To1_19_3;\nimport com.viaversion.viaversion.protocols.v1_19_1to1_19_3.packet.ClientboundPackets1_19_3;\nimport com.viaversion.viaversion.protocols.v1_19_1to1_19_3.packet.ServerboundPackets1_19_3;\nimport com.viaversion.viaversion.protocols.v1_19to1_19_1.packet.ClientboundPackets1_19_1;\nimport com.viaversion.viaversion.protocols.v1_19to1_19_1.packet.ServerboundPackets1_19_1;\nimport com.viaversion.viaversion.rewriter.BlockRewriter;\nimport com.viaversion.viaversion.rewriter.CommandRewriter;\nimport com.viaversion.viaversion.rewriter.ParticleRewriter;\nimport com.viaversion.viaversion.rewriter.TagRewriter;\nimport com.viaversion.viaversion.rewriter.text.ComponentRewriterBase;\nimport com.viaversion.viaversion.util.CipherUtil;\nimport com.viaversion.viaversion.util.ComponentUtil;\nimport com.viaversion.viaversion.util.Pair;\nimport java.security.SignatureException;\nimport java.util.BitSet;\nimport java.util.List;\nimport org.checkerframework.checker.nullness.qual.Nullable;\n\npublic final class Protocol1_19_3To1_19_1 extends BackwardsProtocol<ClientboundPackets1_19_3, ClientboundPackets1_19_1, ServerboundPackets1_19_3, ServerboundPackets1_19_1> {\n\n    public static final BackwardsMappingData MAPPINGS = new BackwardsMappingData(\"1.19.3\", \"1.19\", Protocol1_19_1To1_19_3.class);\n    public static final ByteArrayType.OptionalByteArrayType OPTIONAL_SIGNATURE_BYTES_TYPE = new ByteArrayType.OptionalByteArrayType(256);\n    public static final ByteArrayType SIGNATURE_BYTES_TYPE = new ByteArrayType(256);\n    private final EntityPacketRewriter1_19_3 entityRewriter = new EntityPacketRewriter1_19_3(this);\n    private final BlockItemPacketRewriter1_19_3 itemRewriter = new BlockItemPacketRewriter1_19_3(this);\n    private final ParticleRewriter<ClientboundPackets1_19_3> particleRewriter = new ParticleRewriter<>(this);\n    private final JsonNBTComponentRewriter<ClientboundPackets1_19_3> translatableRewriter = new JsonNBTComponentRewriter<>(this, ComponentRewriterBase.ReadType.JSON);\n    private final TagRewriter<ClientboundPackets1_19_3> tagRewriter = new TagRewriter<>(this);\n    private final BlockRewriter<ClientboundPackets1_19_3> blockRewriter = BlockRewriter.for1_18(this, ChunkType1_18::new);\n\n    public Protocol1_19_3To1_19_1() {\n        super(ClientboundPackets1_19_3.class, ClientboundPackets1_19_1.class, ServerboundPackets1_19_3.class, ServerboundPackets1_19_1.class);\n    }\n\n    @Override\n    protected void registerPackets() {\n        super.registerPackets();\n\n        replaceClientbound(ClientboundPackets1_19_3.SOUND, wrapper -> {\n            final String mappedIdentifier = rewriteSound(wrapper);\n            if (mappedIdentifier != null) {\n                wrapper.write(Types.STRING, mappedIdentifier);\n                wrapper.setPacketType(ClientboundPackets1_19_1.CUSTOM_SOUND);\n            }\n        });\n        replaceClientbound(ClientboundPackets1_19_3.SOUND_ENTITY, wrapper -> {\n            final String mappedIdentifier = rewriteSound(wrapper);\n            if (mappedIdentifier == null) {\n                return;\n            }\n\n            final int mappedId = MAPPINGS.getFullSoundMappings().mappedId(mappedIdentifier);\n            if (mappedId == -1) {\n                wrapper.cancel();\n                return;\n            }\n\n            wrapper.write(Types.VAR_INT, mappedId);\n        });\n\n        tagRewriter.addEmptyTag(RegistryType.BLOCK, \"minecraft:non_flammable_wood\");\n        tagRewriter.addEmptyTag(RegistryType.ITEM, \"minecraft:overworld_natural_logs\");\n\n        final CommandRewriter<ClientboundPackets1_19_3> commandRewriter = new CommandRewriter<>(this);\n        replaceClientbound(ClientboundPackets1_19_3.COMMANDS, wrapper -> {\n            final int size = wrapper.passthrough(Types.VAR_INT);\n            for (int i = 0; i < size; i++) {\n                final byte flags = wrapper.passthrough(Types.BYTE);\n                wrapper.passthrough(Types.VAR_INT_ARRAY_PRIMITIVE); // Children indices\n                if ((flags & 0x08) != 0) {\n                    wrapper.passthrough(Types.VAR_INT); // Redirect node index\n                }\n\n                final int nodeType = flags & 0x03;\n                if (nodeType == 1 || nodeType == 2) { // Literal/argument node\n                    wrapper.passthrough(Types.STRING); // Name\n                }\n\n                if (nodeType == 2) { // Argument node\n                    final int argumentTypeId = wrapper.read(Types.VAR_INT);\n                    final int mappedArgumentTypeId = MAPPINGS.getArgumentTypeMappings().getNewId(argumentTypeId);\n                    Preconditions.checkArgument(mappedArgumentTypeId != -1, \"Unknown command argument type id: \" + argumentTypeId);\n                    wrapper.write(Types.VAR_INT, mappedArgumentTypeId);\n\n                    final String identifier = MAPPINGS.getArgumentTypeMappings().identifier(argumentTypeId);\n                    commandRewriter.handleArgument(wrapper, identifier);\n                    if (identifier.equals(\"minecraft:gamemode\")) {\n                        wrapper.write(Types.VAR_INT, 0); // Word\n                    }\n\n                    if ((flags & 0x10) != 0) {\n                        wrapper.passthrough(Types.STRING); // Suggestion type\n                    }\n                }\n            }\n\n            wrapper.passthrough(Types.VAR_INT); // Root node index\n        });\n\n        replaceClientbound(ClientboundPackets1_19_3.SERVER_DATA, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.OPTIONAL_COMPONENT); // Motd\n                map(Types.OPTIONAL_STRING); // Encoded icon\n                create(Types.BOOLEAN, false); // Previews chat\n            }\n        });\n\n        // Remove the key once again\n        registerServerbound(State.LOGIN, ServerboundLoginPackets.HELLO, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.STRING); // Name\n                handler(wrapper -> {\n                    final ProfileKey profileKey = wrapper.read(Types.OPTIONAL_PROFILE_KEY);\n                    if (profileKey == null) {\n                        wrapper.user().put(new NonceStorage(null));\n                    }\n                });\n            }\n        });\n        registerClientbound(State.LOGIN, ClientboundLoginPackets.HELLO, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.STRING); // Server id\n                handler(wrapper -> {\n                    if (wrapper.user().has(NonceStorage.class)) {\n                        return;\n                    }\n\n                    final byte[] publicKey = wrapper.passthrough(Types.BYTE_ARRAY_PRIMITIVE);\n                    final byte[] nonce = wrapper.passthrough(Types.BYTE_ARRAY_PRIMITIVE);\n                    wrapper.user().put(new NonceStorage(CipherUtil.encryptNonce(publicKey, nonce)));\n                });\n            }\n        });\n        registerServerbound(State.LOGIN, ServerboundLoginPackets.ENCRYPTION_KEY, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.BYTE_ARRAY_PRIMITIVE); // Keys\n                handler(wrapper -> {\n                    final NonceStorage nonceStorage = wrapper.user().remove(NonceStorage.class);\n                    final boolean isNonce = wrapper.read(Types.BOOLEAN);\n                    if (!isNonce) {\n                        wrapper.read(Types.LONG); // Salt\n                        wrapper.read(Types.BYTE_ARRAY_PRIMITIVE); // Signature\n                        wrapper.write(Types.BYTE_ARRAY_PRIMITIVE, nonceStorage.nonce() != null ? nonceStorage.nonce() : new byte[0]);\n                    }\n                });\n            }\n        });\n\n        registerServerbound(ServerboundPackets1_19_1.CHAT, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.STRING); // Message\n                map(Types.LONG); // Timestamp\n                map(Types.LONG); // Salt\n                read(Types.BYTE_ARRAY_PRIMITIVE); // Signature\n                read(Types.BOOLEAN); // Signed preview\n                read(Types.PLAYER_MESSAGE_SIGNATURE_ARRAY); // Last seen messages\n                read(Types.OPTIONAL_PLAYER_MESSAGE_SIGNATURE); // Last received message\n                handler(wrapper -> {\n                    final ChatSession1_19_3 chatSession = wrapper.user().get(ChatSession1_19_3.class);\n\n                    if (chatSession != null) {\n                        final String message = wrapper.get(Types.STRING, 0);\n                        final long timestamp = wrapper.get(Types.LONG, 0);\n                        final long salt = wrapper.get(Types.LONG, 1);\n\n                        final MessageMetadata metadata = new MessageMetadata(null, timestamp, salt);\n                        final byte[] signature;\n                        try {\n                            signature = chatSession.signChatMessage(metadata, message, new PlayerMessageSignature[0]);\n                        } catch (final SignatureException e) {\n                            throw new RuntimeException(e);\n                        }\n\n                        wrapper.write(Protocol1_19_3To1_19_1.OPTIONAL_SIGNATURE_BYTES_TYPE, signature); // Signature\n                    } else {\n                        wrapper.write(Protocol1_19_3To1_19_1.OPTIONAL_SIGNATURE_BYTES_TYPE, null); // Signature\n                    }\n\n                    //TODO is this fine (probably not)? same for chat_command\n                    wrapper.write(Types.VAR_INT, 0); // Offset\n                    wrapper.write(new BitSetType(20), new BitSet(20)); // Acknowledged\n                });\n            }\n        });\n        registerServerbound(ServerboundPackets1_19_1.CHAT_COMMAND, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.STRING); // Command\n                map(Types.LONG); // Timestamp\n                map(Types.LONG); // Salt\n                handler(wrapper -> {\n                    final ChatSession1_19_3 chatSession = wrapper.user().get(ChatSession1_19_3.class);\n                    final SignableCommandArgumentsProvider argumentsProvider = Via.getManager().getProviders().get(SignableCommandArgumentsProvider.class);\n\n                    final String command = wrapper.get(Types.STRING, 0);\n                    final long timestamp = wrapper.get(Types.LONG, 0);\n                    final long salt = wrapper.get(Types.LONG, 1);\n\n                    final int signatures = wrapper.read(Types.VAR_INT);\n                    for (int i = 0; i < signatures; i++) {\n                        wrapper.read(Types.STRING); // Name\n                        wrapper.read(Types.BYTE_ARRAY_PRIMITIVE); // Signature\n                    }\n                    wrapper.read(Types.BOOLEAN); // Signed preview\n\n                    if (chatSession != null && argumentsProvider != null) {\n                        final MessageMetadata metadata = new MessageMetadata(null, timestamp, salt);\n\n                        final List<Pair<String, String>> arguments = argumentsProvider.getSignableArguments(command);\n                        wrapper.write(Types.VAR_INT, arguments.size());\n                        for (final Pair<String, String> argument : arguments) {\n                            final byte[] signature;\n                            try {\n                                signature = chatSession.signChatMessage(metadata, argument.value(), new PlayerMessageSignature[0]);\n                            } catch (final SignatureException e) {\n                                throw new RuntimeException(e);\n                            }\n\n                            wrapper.write(Types.STRING, argument.key());\n                            wrapper.write(Protocol1_19_3To1_19_1.SIGNATURE_BYTES_TYPE, signature);\n                        }\n                    } else {\n                        wrapper.write(Types.VAR_INT, 0); // No signatures\n                    }\n\n                    final int offset = 0;\n                    final BitSet acknowledged = new BitSet(20);\n                    wrapper.write(Types.VAR_INT, offset);\n                    wrapper.write(new BitSetType(20), acknowledged);\n                });\n                read(Types.PLAYER_MESSAGE_SIGNATURE_ARRAY); // Last seen messages\n                read(Types.OPTIONAL_PLAYER_MESSAGE_SIGNATURE); // Last received message\n            }\n        });\n        registerClientbound(ClientboundPackets1_19_3.PLAYER_CHAT, ClientboundPackets1_19_1.SYSTEM_CHAT, new PacketHandlers() {\n            @Override\n            public void register() {\n                read(Types.UUID); // Sender\n                read(Types.VAR_INT); // Index\n                read(OPTIONAL_SIGNATURE_BYTES_TYPE); // Signature\n                handler(wrapper -> {\n                    final String plainContent = wrapper.read(Types.STRING);\n                    wrapper.read(Types.LONG); // Timestamp\n                    wrapper.read(Types.LONG); // Salt\n                    final int lastSeen = wrapper.read(Types.VAR_INT);\n                    for (int i = 0; i < lastSeen; i++) {\n                        final int index = wrapper.read(Types.VAR_INT);\n                        if (index == 0) {\n                            wrapper.read(SIGNATURE_BYTES_TYPE);\n                        }\n                    }\n\n                    final JsonElement unsignedContent = wrapper.read(Types.OPTIONAL_COMPONENT);\n                    final JsonElement content = unsignedContent != null ? unsignedContent : ComponentUtil.plainToJson(plainContent);\n                    translatableRewriter.processText(wrapper.user(), content);\n                    final int filterMaskType = wrapper.read(Types.VAR_INT);\n                    if (filterMaskType == 2) {\n                        wrapper.read(Types.LONG_ARRAY_PRIMITIVE); // Mask\n                    }\n\n                    final int chatTypeId = wrapper.read(Types.VAR_INT);\n                    final JsonElement senderName = wrapper.read(Types.COMPONENT);\n                    final JsonElement targetName = wrapper.read(Types.OPTIONAL_COMPONENT);\n                    final JsonElement result = Protocol1_19_1To1_19.decorateChatMessage(Protocol1_19_3To1_19_1.this, wrapper.user().get(ChatTypeStorage1_19_3.class), chatTypeId, senderName, targetName, content);\n                    if (result == null) {\n                        wrapper.cancel();\n                        return;\n                    }\n\n                    wrapper.write(Types.COMPONENT, result);\n                    wrapper.write(Types.BOOLEAN, false);\n                });\n            }\n        });\n        registerClientbound(ClientboundPackets1_19_3.DISGUISED_CHAT, ClientboundPackets1_19_1.SYSTEM_CHAT, wrapper -> {\n            final JsonElement content = wrapper.read(Types.COMPONENT);\n            translatableRewriter.processText(wrapper.user(), content);\n            final int chatTypeId = wrapper.read(Types.VAR_INT);\n            final JsonElement senderName = wrapper.read(Types.COMPONENT);\n            final JsonElement targetName = wrapper.read(Types.OPTIONAL_COMPONENT);\n            final JsonElement result = Protocol1_19_1To1_19.decorateChatMessage(this, wrapper.user().get(ChatTypeStorage1_19_3.class), chatTypeId, senderName, targetName, content);\n            if (result == null) {\n                wrapper.cancel();\n                return;\n            }\n\n            wrapper.write(Types.COMPONENT, result);\n            wrapper.write(Types.BOOLEAN, false);\n        });\n\n        cancelClientbound(ClientboundPackets1_19_3.UPDATE_ENABLED_FEATURES);\n        cancelServerbound(ServerboundPackets1_19_1.CHAT_PREVIEW);\n        cancelServerbound(ServerboundPackets1_19_1.CHAT_ACK);\n    }\n\n    private @Nullable String rewriteSound(final PacketWrapper wrapper) {\n        final Holder<SoundEvent> holder = wrapper.read(Types.SOUND_EVENT);\n        if (holder.hasId()) {\n            final int mappedId = MAPPINGS.getSoundMappings().getNewId(holder.id());\n            if (mappedId == -1) {\n                wrapper.cancel();\n                return null;\n            }\n\n            wrapper.write(Types.VAR_INT, mappedId);\n            return null;\n        }\n\n        // Convert the resource location to the corresponding integer id\n        final String soundIdentifier = holder.value().identifier();\n        final String mappedIdentifier = MAPPINGS.getMappedNamedSound(soundIdentifier);\n        if (mappedIdentifier == null) {\n            return soundIdentifier;\n        }\n\n        if (mappedIdentifier.isEmpty()) {\n            wrapper.cancel();\n            return null;\n        }\n\n        return mappedIdentifier;\n    }\n\n    @Override\n    public void init(final UserConnection user) {\n        user.put(new ChatSessionStorage());\n        user.put(new ChatTypeStorage1_19_3());\n        addEntityTracker(user, new EntityTrackerBase(user, EntityTypes1_19_3.PLAYER));\n    }\n\n    @Override\n    public BackwardsMappingData getMappingData() {\n        return MAPPINGS;\n    }\n\n    @Override\n    public JsonNBTComponentRewriter<ClientboundPackets1_19_3> getComponentRewriter() {\n        return translatableRewriter;\n    }\n\n    @Override\n    public BlockItemPacketRewriter1_19_3 getItemRewriter() {\n        return itemRewriter;\n    }\n\n    @Override\n    public BlockRewriter<ClientboundPackets1_19_3> getBlockRewriter() {\n        return blockRewriter;\n    }\n\n    @Override\n    public ParticleRewriter<ClientboundPackets1_19_3> getParticleRewriter() {\n        return particleRewriter;\n    }\n\n    @Override\n    public EntityPacketRewriter1_19_3 getEntityRewriter() {\n        return entityRewriter;\n    }\n\n    @Override\n    public TagRewriter<ClientboundPackets1_19_3> getTagRewriter() {\n        return tagRewriter;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_19_3to1_19_1/rewriter/BlockItemPacketRewriter1_19_3.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_19_3to1_19_1.rewriter;\n\nimport com.viaversion.viabackwards.api.rewriters.BackwardsItemRewriter;\nimport com.viaversion.viabackwards.protocol.v1_19_3to1_19_1.Protocol1_19_3To1_19_1;\nimport com.viaversion.viaversion.api.minecraft.item.Item;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.protocols.v1_19_1to1_19_3.packet.ClientboundPackets1_19_3;\nimport com.viaversion.viaversion.protocols.v1_19to1_19_1.packet.ServerboundPackets1_19_1;\nimport com.viaversion.viaversion.rewriter.RecipeRewriter;\nimport com.viaversion.viaversion.util.Key;\n\npublic final class BlockItemPacketRewriter1_19_3 extends BackwardsItemRewriter<ClientboundPackets1_19_3, ServerboundPackets1_19_1, Protocol1_19_3To1_19_1> {\n\n    public BlockItemPacketRewriter1_19_3(final Protocol1_19_3To1_19_1 protocol) {\n        super(protocol, Types.ITEM1_13_2, Types.ITEM1_13_2_ARRAY);\n    }\n\n    @Override\n    protected void registerPackets() {\n        protocol.registerClientbound(ClientboundPackets1_19_3.EXPLODE, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.DOUBLE, Types.FLOAT); // X\n                map(Types.DOUBLE, Types.FLOAT); // Y\n                map(Types.DOUBLE, Types.FLOAT); // Z\n            }\n        });\n\n        final RecipeRewriter<ClientboundPackets1_19_3> recipeRewriter = new RecipeRewriter<>(protocol);\n        protocol.registerClientbound(ClientboundPackets1_19_3.UPDATE_RECIPES, wrapper -> {\n            final int size = wrapper.passthrough(Types.VAR_INT);\n            for (int i = 0; i < size; i++) {\n                final String type = Key.stripMinecraftNamespace(wrapper.passthrough(Types.STRING));\n                wrapper.passthrough(Types.STRING); // Recipe Identifier\n                switch (type) {\n                    case \"crafting_shapeless\" -> {\n                        wrapper.passthrough(Types.STRING); // Group\n                        wrapper.read(Types.VAR_INT); // Crafting book category\n                        final int ingredients = wrapper.passthrough(Types.VAR_INT);\n                        for (int j = 0; j < ingredients; j++) {\n                            final Item[] items = wrapper.passthrough(Types.ITEM1_13_2_ARRAY); // Ingredients\n                            for (int k = 0; k < items.length; k++) {\n                                items[k] = handleItemToClient(wrapper.user(), items[k]);\n                            }\n                        }\n                        passthroughClientboundItem(wrapper); // Result\n                    }\n                    case \"crafting_shaped\" -> {\n                        final int ingredients = wrapper.passthrough(Types.VAR_INT) * wrapper.passthrough(Types.VAR_INT);\n                        wrapper.passthrough(Types.STRING); // Group\n                        wrapper.read(Types.VAR_INT); // Crafting book category\n                        for (int j = 0; j < ingredients; j++) {\n                            final Item[] items = wrapper.passthrough(Types.ITEM1_13_2_ARRAY); // Ingredients\n                            for (int k = 0; k < items.length; k++) {\n                                items[k] = handleItemToClient(wrapper.user(), items[k]);\n                            }\n                        }\n                        passthroughClientboundItem(wrapper); // Result\n                    }\n                    case \"smelting\", \"campfire_cooking\", \"blasting\", \"smoking\" -> {\n                        wrapper.passthrough(Types.STRING); // Group\n                        wrapper.read(Types.VAR_INT); // Crafting book category\n                        final Item[] items = wrapper.passthrough(Types.ITEM1_13_2_ARRAY); // Ingredients\n                        for (int j = 0; j < items.length; j++) {\n                            items[j] = handleItemToClient(wrapper.user(), items[j]);\n                        }\n                        passthroughClientboundItem(wrapper); // Result\n                        wrapper.passthrough(Types.FLOAT); // EXP\n                        wrapper.passthrough(Types.VAR_INT); // Cooking time\n                    }\n                    case \"crafting_special_armordye\", \"crafting_special_bookcloning\", \"crafting_special_mapcloning\",\n                         \"crafting_special_mapextending\", \"crafting_special_firework_rocket\",\n                         \"crafting_special_firework_star\",\n                         \"crafting_special_firework_star_fade\", \"crafting_special_tippedarrow\",\n                         \"crafting_special_bannerduplicate\",\n                         \"crafting_special_shielddecoration\", \"crafting_special_shulkerboxcoloring\",\n                         \"crafting_special_suspiciousstew\",\n                         \"crafting_special_repairitem\" -> wrapper.read(Types.VAR_INT); // Crafting book category\n                    default -> recipeRewriter.handleRecipeType(wrapper, type);\n                }\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_19_3to1_19_1/rewriter/EntityPacketRewriter1_19_3.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_19_3to1_19_1.rewriter;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.ListTag;\nimport com.viaversion.nbt.tag.NumberTag;\nimport com.viaversion.viabackwards.api.rewriters.EntityRewriter;\nimport com.viaversion.viabackwards.protocol.v1_19_3to1_19_1.Protocol1_19_3To1_19_1;\nimport com.viaversion.viabackwards.protocol.v1_19_3to1_19_1.storage.ChatTypeStorage1_19_3;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.ProfileKey;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityType;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_19_3;\nimport com.viaversion.viaversion.api.minecraft.signature.storage.ChatSession1_19_3;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.version.Types1_19;\nimport com.viaversion.viaversion.api.type.types.version.Types1_19_3;\nimport com.viaversion.viaversion.libs.gson.JsonElement;\nimport com.viaversion.viaversion.protocols.v1_19_1to1_19_3.packet.ClientboundPackets1_19_3;\nimport com.viaversion.viaversion.protocols.v1_19_1to1_19_3.packet.ServerboundPackets1_19_3;\nimport com.viaversion.viaversion.protocols.v1_19to1_19_1.packet.ClientboundPackets1_19_1;\nimport com.viaversion.viaversion.util.TagUtil;\nimport java.util.BitSet;\nimport java.util.UUID;\nimport org.checkerframework.checker.nullness.qual.Nullable;\n\npublic final class EntityPacketRewriter1_19_3 extends EntityRewriter<ClientboundPackets1_19_3, Protocol1_19_3To1_19_1> {\n\n    private static final int[] PROFILE_ACTIONS = {2, 3, 4, 5}; // Ignore initialize chat; add player already handled before\n    private static final int ADD_PLAYER = 0;\n    private static final int INITIALIZE_CHAT = 1;\n    private static final int UPDATE_GAMEMODE = 2;\n    private static final int UPDATE_LISTED = 3;\n    private static final int UPDATE_LATENCY = 4;\n    private static final int UPDATE_DISPLAYNAME = 5;\n\n    public EntityPacketRewriter1_19_3(final Protocol1_19_3To1_19_1 protocol) {\n        super(protocol, Types1_19.ENTITY_DATA_TYPES.optionalComponentType, Types1_19.ENTITY_DATA_TYPES.booleanType);\n    }\n\n    @Override\n    protected void registerPackets() {\n        registerSetEntityData(ClientboundPackets1_19_3.SET_ENTITY_DATA, Types1_19_3.ENTITY_DATA_LIST, Types1_19.ENTITY_DATA_LIST);\n\n        protocol.registerClientbound(ClientboundPackets1_19_3.LOGIN, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.INT); // Entity id\n                map(Types.BOOLEAN); // Hardcore\n                map(Types.BYTE); // Gamemode\n                map(Types.BYTE); // Previous Gamemode\n                map(Types.STRING_ARRAY); // World List\n                map(Types.NAMED_COMPOUND_TAG); // Dimension registry\n                map(Types.STRING); // Dimension key\n                map(Types.STRING); // World\n                handler(dimensionDataHandler());\n                handler(biomeSizeTracker());\n                handler(worldDataTrackerHandlerByKey());\n                handler(wrapper -> {\n                    final ChatTypeStorage1_19_3 chatTypeStorage = wrapper.user().get(ChatTypeStorage1_19_3.class);\n                    chatTypeStorage.clear();\n\n                    final CompoundTag registry = wrapper.get(Types.NAMED_COMPOUND_TAG, 0);\n                    final ListTag<CompoundTag> chatTypes = TagUtil.getRegistryEntries(registry, \"chat_type\", new ListTag<>(CompoundTag.class));\n                    for (final CompoundTag chatType : chatTypes) {\n                        final NumberTag idTag = chatType.getNumberTag(\"id\");\n                        chatTypeStorage.addChatType(idTag.asInt(), chatType);\n                    }\n                });\n                handler(wrapper -> {\n                    final ChatSession1_19_3 chatSession = wrapper.user().get(ChatSession1_19_3.class);\n\n                    if (chatSession != null) {\n                        final PacketWrapper chatSessionUpdate = wrapper.create(ServerboundPackets1_19_3.CHAT_SESSION_UPDATE);\n                        chatSessionUpdate.write(Types.UUID, chatSession.getSessionId());\n                        chatSessionUpdate.write(Types.PROFILE_KEY, chatSession.getProfileKey());\n                        chatSessionUpdate.sendToServer(Protocol1_19_3To1_19_1.class);\n                    }\n                });\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_19_3.RESPAWN, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.STRING); // Dimension\n                map(Types.STRING); // World\n                map(Types.LONG); // Seed\n                map(Types.UNSIGNED_BYTE); // Gamemode\n                map(Types.BYTE); // Previous gamemode\n                map(Types.BOOLEAN); // Debug\n                map(Types.BOOLEAN); // Flat\n                handler(worldDataTrackerHandlerByKey());\n                handler(wrapper -> {\n                    // Old clients will always keep entity data (packed here as 0x02), nothing we can do there\n                    final byte keepDataMask = wrapper.read(Types.BYTE);\n                    wrapper.write(Types.BOOLEAN, (keepDataMask & 1) != 0); // Keep attributes\n                });\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_19_3.PLAYER_INFO_UPDATE, ClientboundPackets1_19_1.PLAYER_INFO, wrapper -> {\n            wrapper.cancel();\n            final BitSet actions = wrapper.read(Types.PROFILE_ACTIONS_ENUM1_19_3);\n            final int entries = wrapper.read(Types.VAR_INT);\n            if (actions.get(ADD_PLAYER)) {\n                // Special case, as we need to write everything into one action\n                final PacketWrapper playerInfoPacket = wrapper.create(ClientboundPackets1_19_1.PLAYER_INFO);\n                playerInfoPacket.write(Types.VAR_INT, 0);\n                playerInfoPacket.write(Types.VAR_INT, entries);\n                for (int i = 0; i < entries; i++) {\n                    playerInfoPacket.write(Types.UUID, wrapper.read(Types.UUID));\n                    playerInfoPacket.write(Types.STRING, wrapper.read(Types.STRING)); // Player Name\n                    playerInfoPacket.write(Types.PROFILE_PROPERTY_ARRAY, wrapper.read(Types.PROFILE_PROPERTY_ARRAY));\n\n                    // Now check for the other parts individually and add dummy values if not present\n                    final ProfileKey profileKey;\n                    if (actions.get(INITIALIZE_CHAT) && wrapper.read(Types.BOOLEAN)) {\n                        wrapper.read(Types.UUID); // Session UUID\n                        profileKey = wrapper.read(Types.PROFILE_KEY);\n                    } else {\n                        profileKey = null;\n                    }\n\n                    final int gamemode = actions.get(UPDATE_GAMEMODE) ? wrapper.read(Types.VAR_INT) : 0;\n\n                    if (actions.get(UPDATE_LISTED)) {\n                        wrapper.read(Types.BOOLEAN); // Listed - throw away\n                    }\n\n                    final int latency = actions.get(UPDATE_LATENCY) ? wrapper.read(Types.VAR_INT) : 0;\n\n                    final JsonElement displayName = actions.get(UPDATE_DISPLAYNAME) ? wrapper.read(Types.OPTIONAL_COMPONENT) : null;\n                    playerInfoPacket.write(Types.VAR_INT, gamemode);\n                    playerInfoPacket.write(Types.VAR_INT, latency);\n                    playerInfoPacket.write(Types.OPTIONAL_COMPONENT, displayName);\n                    playerInfoPacket.write(Types.OPTIONAL_PROFILE_KEY, profileKey);\n                }\n                playerInfoPacket.send(Protocol1_19_3To1_19_1.class);\n                return;\n            }\n\n            final PlayerProfileUpdate[] updates = new PlayerProfileUpdate[entries];\n            for (int i = 0; i < entries; i++) {\n                final UUID uuid = wrapper.read(Types.UUID);\n                int gamemode = 0;\n                int latency = 0;\n                JsonElement displayName = null;\n                for (final int action : PROFILE_ACTIONS) {\n                    if (!actions.get(action)) {\n                        continue;\n                    }\n                    switch (action) {\n                        case UPDATE_GAMEMODE -> gamemode = wrapper.read(Types.VAR_INT);\n                        case UPDATE_LISTED -> wrapper.read(Types.BOOLEAN); // Throw away\n                        case UPDATE_LATENCY -> latency = wrapper.read(Types.VAR_INT);\n                        case UPDATE_DISPLAYNAME -> displayName = wrapper.read(Types.OPTIONAL_COMPONENT);\n                    }\n                }\n\n                updates[i] = new PlayerProfileUpdate(uuid, gamemode, latency, displayName);\n            }\n\n            if (actions.get(UPDATE_GAMEMODE)) {\n                sendPlayerProfileUpdate(wrapper.user(), 1, updates);\n            } else if (actions.get(UPDATE_LATENCY)) {\n                sendPlayerProfileUpdate(wrapper.user(), 2, updates);\n            } else if (actions.get(UPDATE_DISPLAYNAME)) {\n                sendPlayerProfileUpdate(wrapper.user(), 3, updates);\n            }\n        });\n        protocol.registerClientbound(ClientboundPackets1_19_3.PLAYER_INFO_REMOVE, ClientboundPackets1_19_1.PLAYER_INFO, wrapper -> {\n            final UUID[] uuids = wrapper.read(Types.UUID_ARRAY);\n            wrapper.write(Types.VAR_INT, 4); // Remove player\n            wrapper.write(Types.VAR_INT, uuids.length);\n            for (final UUID uuid : uuids) {\n                wrapper.write(Types.UUID, uuid);\n            }\n        });\n    }\n\n    private void sendPlayerProfileUpdate(final UserConnection connection, final int action, final PlayerProfileUpdate[] updates) {\n        final PacketWrapper playerInfoPacket = PacketWrapper.create(ClientboundPackets1_19_1.PLAYER_INFO, connection);\n        playerInfoPacket.write(Types.VAR_INT, action);\n        playerInfoPacket.write(Types.VAR_INT, updates.length);\n        for (final PlayerProfileUpdate update : updates) {\n            playerInfoPacket.write(Types.UUID, update.uuid());\n            if (action == 1) {\n                playerInfoPacket.write(Types.VAR_INT, update.gamemode());\n            } else if (action == 2) {\n                playerInfoPacket.write(Types.VAR_INT, update.latency());\n            } else if (action == 3) {\n                playerInfoPacket.write(Types.OPTIONAL_COMPONENT, update.displayName());\n            } else {\n                throw new IllegalArgumentException(\"Invalid action: \" + action);\n            }\n        }\n        playerInfoPacket.send(Protocol1_19_3To1_19_1.class);\n    }\n\n    @Override\n    public void registerRewrites() {\n        filter().handler((event, data) -> {\n            final int id = data.dataType().typeId();\n            if (id > 2) {\n                data.setDataType(Types1_19.ENTITY_DATA_TYPES.byId(id - 1)); // long added\n            } else if (id != 2) {\n                data.setDataType(Types1_19.ENTITY_DATA_TYPES.byId(id));\n            }\n        });\n        registerEntityDataTypeHandler(Types1_19.ENTITY_DATA_TYPES.itemType, null, Types1_19.ENTITY_DATA_TYPES.optionalBlockStateType, Types1_19.ENTITY_DATA_TYPES.particleType,\n            Types1_19.ENTITY_DATA_TYPES.componentType, Types1_19.ENTITY_DATA_TYPES.optionalComponentType);\n        registerBlockStateHandler(EntityTypes1_19_3.ABSTRACT_MINECART, 11);\n\n        filter().dataType(Types1_19.ENTITY_DATA_TYPES.poseType).handler((event, data) -> {\n            // Sitting pose added\n            final int pose = data.value();\n            if (pose == 10) {\n                data.setValue(0); // Standing\n            } else if (pose > 10) {\n                data.setValue(pose - 1);\n            }\n        });\n\n        filter().type(EntityTypes1_19_3.CAMEL).cancel(19); // Dashing\n        filter().type(EntityTypes1_19_3.CAMEL).cancel(20); // Last pose change time\n    }\n\n    @Override\n    public void onMappingDataLoaded() {\n        super.onMappingDataLoaded();\n        mapEntityTypeWithData(EntityTypes1_19_3.CAMEL, EntityTypes1_19_3.DONKEY).jsonName();\n    }\n\n    @Override\n    public EntityType typeFromId(final int typeId) {\n        return EntityTypes1_19_3.getTypeFromId(typeId);\n    }\n\n    private record PlayerProfileUpdate(UUID uuid, int gamemode, int latency, @Nullable JsonElement displayName) {\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_19_3to1_19_1/storage/ChatSessionStorage.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_19_3to1_19_1.storage;\n\nimport com.viaversion.viaversion.api.connection.StorableObject;\nimport java.util.UUID;\n\npublic final class ChatSessionStorage implements StorableObject {\n\n    private final UUID uuid = UUID.randomUUID();\n\n    public UUID uuid() {\n        return uuid;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_19_3to1_19_1/storage/ChatTypeStorage1_19_3.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_19_3to1_19_1.storage;\n\nimport com.viaversion.viabackwards.protocol.v1_19_1to1_19.storage.ChatRegistryStorage;\n\npublic final class ChatTypeStorage1_19_3 extends ChatRegistryStorage {\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_19_3to1_19_1/storage/NonceStorage.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_19_3to1_19_1.storage;\n\nimport com.viaversion.viaversion.api.connection.StorableObject;\nimport org.checkerframework.checker.nullness.qual.Nullable;\n\npublic record NonceStorage(byte @Nullable [] nonce) implements StorableObject {\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_19_4to1_19_3/Protocol1_19_4To1_19_3.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_19_4to1_19_3;\n\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viabackwards.api.data.BackwardsMappingData;\nimport com.viaversion.viabackwards.api.rewriters.text.JsonNBTComponentRewriter;\nimport com.viaversion.viabackwards.protocol.v1_19_4to1_19_3.rewriter.BlockItemPacketRewriter1_19_4;\nimport com.viaversion.viabackwards.protocol.v1_19_4to1_19_3.rewriter.EntityPacketRewriter1_19_4;\nimport com.viaversion.viabackwards.protocol.v1_19_4to1_19_3.storage.EntityTracker1_19_4;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.chunk.ChunkType1_18;\nimport com.viaversion.viaversion.libs.gson.JsonElement;\nimport com.viaversion.viaversion.protocols.v1_19_1to1_19_3.packet.ClientboundPackets1_19_3;\nimport com.viaversion.viaversion.protocols.v1_19_1to1_19_3.packet.ServerboundPackets1_19_3;\nimport com.viaversion.viaversion.protocols.v1_19_3to1_19_4.Protocol1_19_3To1_19_4;\nimport com.viaversion.viaversion.protocols.v1_19_3to1_19_4.packet.ClientboundPackets1_19_4;\nimport com.viaversion.viaversion.protocols.v1_19_3to1_19_4.packet.ServerboundPackets1_19_4;\nimport com.viaversion.viaversion.rewriter.BlockRewriter;\nimport com.viaversion.viaversion.rewriter.CommandRewriter;\nimport com.viaversion.viaversion.rewriter.ParticleRewriter;\nimport com.viaversion.viaversion.rewriter.TagRewriter;\nimport com.viaversion.viaversion.rewriter.text.ComponentRewriterBase;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Base64;\n\npublic final class Protocol1_19_4To1_19_3 extends BackwardsProtocol<ClientboundPackets1_19_4, ClientboundPackets1_19_3, ServerboundPackets1_19_4, ServerboundPackets1_19_3> {\n\n    public static final BackwardsMappingData MAPPINGS = new BackwardsMappingData(\"1.19.4\", \"1.19.3\", Protocol1_19_3To1_19_4.class);\n    private final EntityPacketRewriter1_19_4 entityRewriter = new EntityPacketRewriter1_19_4(this);\n    private final BlockItemPacketRewriter1_19_4 itemRewriter = new BlockItemPacketRewriter1_19_4(this);\n    private final ParticleRewriter<ClientboundPackets1_19_4> particleRewriter = new ParticleRewriter<>(this);\n    private final JsonNBTComponentRewriter<ClientboundPackets1_19_4> translatableRewriter = new JsonNBTComponentRewriter<>(this, ComponentRewriterBase.ReadType.JSON);\n    private final TagRewriter<ClientboundPackets1_19_4> tagRewriter = new TagRewriter<>(this);\n    private final BlockRewriter<ClientboundPackets1_19_4> blockRewriter = BlockRewriter.for1_18(this, ChunkType1_18::new);\n\n    public Protocol1_19_4To1_19_3() {\n        super(ClientboundPackets1_19_4.class, ClientboundPackets1_19_3.class, ServerboundPackets1_19_4.class, ServerboundPackets1_19_3.class);\n    }\n\n    @Override\n    protected void registerPackets() {\n        super.registerPackets();\n\n        final CommandRewriter<ClientboundPackets1_19_4> commandRewriter = new CommandRewriter<>(this) {\n            @Override\n            public void handleArgument(final PacketWrapper wrapper, final String argumentType) {\n                switch (argumentType) {\n                    case \"minecraft:heightmap\" -> wrapper.write(Types.VAR_INT, 0);\n                    case \"minecraft:time\" -> wrapper.read(Types.INT); // Minimum\n                    case \"minecraft:resource\", \"minecraft:resource_or_tag\" -> {\n                        final String resource = wrapper.read(Types.STRING);\n                        // Replace damage types with... something\n                        wrapper.write(Types.STRING, resource.equals(\"minecraft:damage_type\") ? \"minecraft:mob_effect\" : resource);\n                    }\n                    default -> super.handleArgument(wrapper, argumentType);\n                }\n            }\n        };\n        replaceClientbound(ClientboundPackets1_19_4.COMMANDS, commandRewriter::handle1_19);\n\n        tagRewriter.removeTags(\"minecraft:damage_type\");\n\n        replaceClientbound(ClientboundPackets1_19_4.SERVER_DATA, wrapper -> {\n            final JsonElement element = wrapper.read(Types.COMPONENT);\n            translatableRewriter.processText(wrapper.user(), element);\n            wrapper.write(Types.OPTIONAL_COMPONENT, element);\n\n            final byte[] iconBytes = wrapper.read(Types.OPTIONAL_BYTE_ARRAY_PRIMITIVE);\n            final String iconBase64 = iconBytes != null ? \"data:image/png;base64,\" + new String(Base64.getEncoder().encode(iconBytes), StandardCharsets.UTF_8) : null;\n            wrapper.write(Types.OPTIONAL_STRING, iconBase64);\n        });\n\n        cancelClientbound(ClientboundPackets1_19_4.BUNDLE_DELIMITER);\n        cancelClientbound(ClientboundPackets1_19_4.CHUNKS_BIOMES); // We definitely do not want to cache every single chunk just to resent them with new biomes\n    }\n\n    @Override\n    public void init(final UserConnection user) {\n        addEntityTracker(user, new EntityTracker1_19_4(user));\n    }\n\n    @Override\n    public BackwardsMappingData getMappingData() {\n        return MAPPINGS;\n    }\n\n    @Override\n    public BlockItemPacketRewriter1_19_4 getItemRewriter() {\n        return itemRewriter;\n    }\n\n    @Override\n    public BlockRewriter<ClientboundPackets1_19_4> getBlockRewriter() {\n        return blockRewriter;\n    }\n\n    @Override\n    public ParticleRewriter<ClientboundPackets1_19_4> getParticleRewriter() {\n        return particleRewriter;\n    }\n\n    @Override\n    public EntityPacketRewriter1_19_4 getEntityRewriter() {\n        return entityRewriter;\n    }\n\n    @Override\n    public JsonNBTComponentRewriter<ClientboundPackets1_19_4> getComponentRewriter() {\n        return translatableRewriter;\n    }\n\n    @Override\n    public TagRewriter<ClientboundPackets1_19_4> getTagRewriter() {\n        return tagRewriter;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_19_4to1_19_3/rewriter/BlockItemPacketRewriter1_19_4.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_19_4to1_19_3.rewriter;\n\nimport com.viaversion.viabackwards.api.rewriters.BackwardsItemRewriter;\nimport com.viaversion.viabackwards.protocol.v1_19_4to1_19_3.Protocol1_19_4To1_19_3;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.protocols.v1_19_1to1_19_3.packet.ServerboundPackets1_19_3;\nimport com.viaversion.viaversion.protocols.v1_19_1to1_19_3.rewriter.RecipeRewriter1_19_3;\nimport com.viaversion.viaversion.protocols.v1_19_3to1_19_4.packet.ClientboundPackets1_19_4;\nimport com.viaversion.viaversion.util.Key;\n\npublic final class BlockItemPacketRewriter1_19_4 extends BackwardsItemRewriter<ClientboundPackets1_19_4, ServerboundPackets1_19_3, Protocol1_19_4To1_19_3> {\n\n    public BlockItemPacketRewriter1_19_4(final Protocol1_19_4To1_19_3 protocol) {\n        super(protocol, Types.ITEM1_13_2, Types.ITEM1_13_2_ARRAY);\n    }\n\n    @Override\n    public void registerPackets() {\n        protocol.replaceClientbound(ClientboundPackets1_19_4.OPEN_SCREEN, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT); // Container id\n                map(Types.VAR_INT); // Container type\n                map(Types.COMPONENT); // Title\n                handler(wrapper -> {\n                    final int windowType = wrapper.get(Types.VAR_INT, 1);\n                    if (windowType == 21) { // New smithing menu\n                        wrapper.cancel();\n                    } else if (windowType > 21) {\n                        wrapper.set(Types.VAR_INT, 1, windowType - 1);\n                    }\n\n                    protocol.getComponentRewriter().processText(wrapper.user(), wrapper.get(Types.COMPONENT, 0));\n                });\n            }\n        });\n\n        final RecipeRewriter1_19_3<ClientboundPackets1_19_4> recipeRewriter = new RecipeRewriter1_19_3<>(protocol) {\n            @Override\n            public void handleCraftingShaped(final PacketWrapper wrapper) {\n                final int ingredients = wrapper.passthrough(Types.VAR_INT) * wrapper.passthrough(Types.VAR_INT);\n                wrapper.passthrough(Types.STRING); // Group\n                wrapper.passthrough(Types.VAR_INT); // Crafting book category\n                for (int i = 0; i < ingredients; i++) {\n                    handleIngredient(wrapper);\n                }\n                rewrite(wrapper.user(), wrapper.passthrough(Types.ITEM1_13_2)); // Result\n\n                // Remove notification boolean\n                wrapper.read(Types.BOOLEAN);\n            }\n        };\n        protocol.registerClientbound(ClientboundPackets1_19_4.UPDATE_RECIPES, wrapper -> {\n            final int size = wrapper.passthrough(Types.VAR_INT);\n            int newSize = size;\n            for (int i = 0; i < size; i++) {\n                final String type = wrapper.read(Types.STRING);\n                final String cutType = Key.stripMinecraftNamespace(type);\n                if (cutType.equals(\"smithing_transform\") || cutType.equals(\"smithing_trim\")) {\n                    newSize--;\n                    wrapper.read(Types.STRING); // Recipe identifier\n                    wrapper.read(Types.ITEM1_13_2_ARRAY); // Template\n                    wrapper.read(Types.ITEM1_13_2_ARRAY); // Base\n                    wrapper.read(Types.ITEM1_13_2_ARRAY); // Additions\n                    if (cutType.equals(\"smithing_transform\")) {\n                        wrapper.read(Types.ITEM1_13_2); // Result\n                    }\n                    continue;\n                } else if (cutType.equals(\"crafting_decorated_pot\")) {\n                    newSize--;\n                    wrapper.read(Types.STRING); // Recipe identifier\n                    wrapper.read(Types.VAR_INT); // Crafting book category\n                    continue;\n                }\n\n                wrapper.write(Types.STRING, type);\n                wrapper.passthrough(Types.STRING); // Recipe Identifier\n                recipeRewriter.handleRecipeType(wrapper, cutType);\n            }\n\n            wrapper.set(Types.VAR_INT, 0, newSize);\n        });\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_19_4to1_19_3/rewriter/EntityPacketRewriter1_19_4.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_19_4to1_19_3.rewriter;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.ListTag;\nimport com.viaversion.nbt.tag.NumberTag;\nimport com.viaversion.viabackwards.ViaBackwards;\nimport com.viaversion.viabackwards.api.entities.storage.EntityPositionHandler;\nimport com.viaversion.viabackwards.api.entities.storage.EntityReplacement;\nimport com.viaversion.viabackwards.api.rewriters.EntityRewriter;\nimport com.viaversion.viabackwards.protocol.v1_19_4to1_19_3.Protocol1_19_4To1_19_3;\nimport com.viaversion.viabackwards.protocol.v1_19_4to1_19_3.storage.EntityTracker1_19_4;\nimport com.viaversion.viabackwards.protocol.v1_19_4to1_19_3.storage.LinkedEntityStorage;\nimport com.viaversion.viaversion.api.data.entity.StoredEntityData;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityType;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_19_3;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_19_4;\nimport com.viaversion.viaversion.api.minecraft.entitydata.EntityData;\nimport com.viaversion.viaversion.api.minecraft.item.Item;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandler;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.version.Types1_19_3;\nimport com.viaversion.viaversion.api.type.types.version.Types1_19_4;\nimport com.viaversion.viaversion.protocols.v1_19_1to1_19_3.packet.ClientboundPackets1_19_3;\nimport com.viaversion.viaversion.protocols.v1_19_3to1_19_4.packet.ClientboundPackets1_19_4;\nimport com.viaversion.viaversion.util.TagUtil;\n\npublic final class EntityPacketRewriter1_19_4 extends EntityRewriter<ClientboundPackets1_19_4, Protocol1_19_4To1_19_3> {\n\n    private static final double TEXT_DISPLAY_Y_OFFSET = -0.25; // Move emulated armor stands down to match text display height offsets\n\n    public EntityPacketRewriter1_19_4(final Protocol1_19_4To1_19_3 protocol) {\n        super(protocol, Types1_19_3.ENTITY_DATA_TYPES.optionalComponentType, Types1_19_3.ENTITY_DATA_TYPES.booleanType);\n    }\n\n    @Override\n    public void registerPackets() {\n        registerSetEntityData(ClientboundPackets1_19_4.SET_ENTITY_DATA, Types1_19_4.ENTITY_DATA_LIST, Types1_19_3.ENTITY_DATA_LIST);\n\n        protocol.replaceClientbound(ClientboundPackets1_19_4.ADD_ENTITY, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT); // Entity id\n                map(Types.UUID); // Entity UUID\n                map(Types.VAR_INT); // Entity type\n                map(Types.DOUBLE); // X\n                map(Types.DOUBLE); // Y\n                map(Types.DOUBLE); // Z\n                map(Types.BYTE); // Pitch\n                map(Types.BYTE); // Yaw\n                map(Types.BYTE); // Head yaw\n                map(Types.VAR_INT); // Data\n                handler(wrapper -> {\n                    final int entityId = wrapper.get(Types.VAR_INT, 0);\n                    final int entityType = wrapper.get(Types.VAR_INT, 1);\n\n                    if (!ViaBackwards.getConfig().mapDisplayEntities()) {\n                        if (entityType == EntityTypes1_19_4.BLOCK_DISPLAY.getId() || entityType == EntityTypes1_19_4.ITEM_DISPLAY.getId() || entityType == EntityTypes1_19_4.TEXT_DISPLAY.getId()) {\n                            wrapper.cancel();\n                            return;\n                        }\n                    }\n\n                    final double y = wrapper.get(Types.DOUBLE, 1);\n                    if (entityType == EntityTypes1_19_4.TEXT_DISPLAY.getId()) {\n                        wrapper.set(Types.DOUBLE, 1, y + TEXT_DISPLAY_Y_OFFSET);\n                    }\n\n                    // First track (and remap) entity, then put storage for block display entity\n                    getSpawnTrackerWithDataHandler1_19().handle(wrapper);\n                    if (entityType != EntityTypes1_19_4.BLOCK_DISPLAY.getId()) {\n                        return;\n                    }\n\n                    final StoredEntityData data = tracker(wrapper.user()).entityData(entityId);\n                    if (data != null) {\n                        final LinkedEntityStorage storage = new LinkedEntityStorage();\n                        final double x = wrapper.get(Types.DOUBLE, 0);\n                        final double z = wrapper.get(Types.DOUBLE, 2);\n                        storage.setPosition(x, y, z);\n                        data.put(storage);\n                    }\n                });\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_19_4.LOGIN, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.INT); // Entity id\n                map(Types.BOOLEAN); // Hardcore\n                map(Types.BYTE); // Gamemode\n                map(Types.BYTE); // Previous Gamemode\n                map(Types.STRING_ARRAY); // World List\n                map(Types.NAMED_COMPOUND_TAG); // Dimension registry\n                map(Types.STRING); // Dimension key\n                map(Types.STRING); // World\n                handler(dimensionDataHandler());\n                handler(biomeSizeTracker());\n                handler(worldDataTrackerHandlerByKey());\n                handler(wrapper -> {\n                    final CompoundTag registry = wrapper.get(Types.NAMED_COMPOUND_TAG, 0);\n                    TagUtil.removeNamespaced(registry, \"trim_pattern\");\n                    TagUtil.removeNamespaced(registry, \"trim_material\");\n                    TagUtil.removeNamespaced(registry, \"damage_type\");\n\n                    final ListTag<CompoundTag> biomes = TagUtil.getRegistryEntries(registry, \"worldgen/biome\");\n                    for (final CompoundTag biomeTag : biomes) {\n                        final CompoundTag biomeData = biomeTag.getCompoundTag(\"element\");\n                        final NumberTag hasPrecipitation = biomeData.getNumberTag(\"has_precipitation\");\n                        biomeData.putString(\"precipitation\", hasPrecipitation.asByte() == 1 ? \"rain\" : \"none\");\n                    }\n                });\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_19_4.PLAYER_POSITION, new PacketHandlers() {\n            @Override\n            protected void register() {\n                map(Types.DOUBLE); // X\n                map(Types.DOUBLE); // Y\n                map(Types.DOUBLE); // Z\n                map(Types.FLOAT); // Yaw\n                map(Types.FLOAT); // Pitch\n                map(Types.BYTE); // Relative arguments\n                map(Types.VAR_INT); // Id\n                create(Types.BOOLEAN, false); // Dismount vehicle\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_19_4.DAMAGE_EVENT, ClientboundPackets1_19_3.ENTITY_EVENT, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT, Types.INT); // Entity id\n                read(Types.VAR_INT); // Damage type\n                read(Types.VAR_INT); // Cause entity\n                read(Types.VAR_INT); // Direct cause entity\n                handler(wrapper -> {\n                    // Source position\n                    if (wrapper.read(Types.BOOLEAN)) {\n                        wrapper.read(Types.DOUBLE);\n                        wrapper.read(Types.DOUBLE);\n                        wrapper.read(Types.DOUBLE);\n                    }\n                });\n                create(Types.BYTE, (byte) 2); // Generic hurt\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_19_4.HURT_ANIMATION, ClientboundPackets1_19_3.ANIMATE, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT); // Entity id\n                read(Types.FLOAT); // Yaw\n                create(Types.UNSIGNED_BYTE, (short) 1); // Hit\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_19_4.RESPAWN, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.STRING); // Dimension\n                map(Types.STRING); // World\n                handler(worldDataTrackerHandlerByKey());\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_19_4.UPDATE_MOB_EFFECT, wrapper -> {\n            wrapper.passthrough(Types.VAR_INT); // Entity id\n            wrapper.passthrough(Types.VAR_INT); // Effect id\n            wrapper.passthrough(Types.BYTE); // Amplifier\n\n            // Handle infinite duration. Use a value the client still accepts without bugging out the display while still being practically infinite\n            final int duration = wrapper.read(Types.VAR_INT);\n            wrapper.write(Types.VAR_INT, duration == -1 ? 999999 : duration);\n        });\n\n        // Track the position of block display entities to later spawn the linked entities, we will put them\n        // as passengers but the spawn position needs to be in the players view distance\n\n        protocol.registerClientbound(ClientboundPackets1_19_4.TELEPORT_ENTITY, wrapper -> {\n            final int entityId = wrapper.passthrough(Types.VAR_INT);\n            final double x = wrapper.passthrough(Types.DOUBLE);\n            final double y = wrapper.passthrough(Types.DOUBLE);\n            final double z = wrapper.passthrough(Types.DOUBLE);\n\n            final EntityTracker1_19_4 tracker = tracker(wrapper.user());\n            if (tracker.entityType(entityId) == EntityTypes1_19_4.TEXT_DISPLAY) {\n                wrapper.set(Types.DOUBLE, 1, y + TEXT_DISPLAY_Y_OFFSET);\n            }\n\n            final LinkedEntityStorage storage = tracker.linkedEntityStorage(entityId);\n            if (storage != null) {\n                storage.setPosition(x, y, z);\n            }\n        });\n\n        final PacketHandler entityPositionHandler = wrapper -> {\n            final int entityId = wrapper.passthrough(Types.VAR_INT);\n            final double x = wrapper.passthrough(Types.SHORT) / EntityPositionHandler.RELATIVE_MOVE_FACTOR;\n            final double y = wrapper.passthrough(Types.SHORT) / EntityPositionHandler.RELATIVE_MOVE_FACTOR;\n            final double z = wrapper.passthrough(Types.SHORT) / EntityPositionHandler.RELATIVE_MOVE_FACTOR;\n\n            final EntityTracker1_19_4 tracker = tracker(wrapper.user());\n            final LinkedEntityStorage storage = tracker.linkedEntityStorage(entityId);\n            if (storage != null) {\n                storage.addRelativePosition(x, y, z);\n            }\n        };\n\n        protocol.registerClientbound(ClientboundPackets1_19_4.MOVE_ENTITY_POS, entityPositionHandler);\n        protocol.registerClientbound(ClientboundPackets1_19_4.MOVE_ENTITY_POS_ROT, entityPositionHandler);\n    }\n\n    @Override\n    public void registerRewrites() {\n        filter().handler((event, data) -> {\n            int id = data.dataType().typeId();\n            if (id >= 25) { // Sniffer state, Vector3f, Quaternion types\n                event.cancel();\n                return;\n            } else if (id >= 15) { // Optional block state - just map down to block state\n                id--;\n            }\n\n            data.setDataType(Types1_19_3.ENTITY_DATA_TYPES.byId(id));\n        });\n        registerEntityDataTypeHandler(Types1_19_3.ENTITY_DATA_TYPES.itemType, null, Types1_19_3.ENTITY_DATA_TYPES.optionalBlockStateType, Types1_19_3.ENTITY_DATA_TYPES.particleType,\n            Types1_19_3.ENTITY_DATA_TYPES.componentType, Types1_19_3.ENTITY_DATA_TYPES.optionalComponentType);\n        registerBlockStateHandler(EntityTypes1_19_4.ABSTRACT_MINECART, 11);\n\n        filter().type(EntityTypes1_19_4.BOAT).index(11).handler((event, data) -> {\n            final int boatType = data.value();\n            if (boatType > 4) { // Cherry\n                data.setValue(boatType - 1);\n            }\n        });\n\n        filter().type(EntityTypes1_19_4.TEXT_DISPLAY).index(22).handler(((event, data) -> {\n            // Send as custom display name\n            event.setIndex(2);\n            data.setDataType(Types1_19_3.ENTITY_DATA_TYPES.optionalComponentType);\n            event.createExtraData(new EntityData(3, Types1_19_3.ENTITY_DATA_TYPES.booleanType, true)); // Show custom name\n        }));\n        filter().type(EntityTypes1_19_4.BLOCK_DISPLAY).index(22).handler((event, data) -> {\n            final int value = data.value();\n\n            final EntityTracker1_19_4 tracker = tracker(event.user());\n            tracker.clearLinkedEntities(event.entityId());\n\n            final LinkedEntityStorage storage = tracker.linkedEntityStorage(event.entityId());\n            if (storage == null) {\n                return;\n            }\n            final int linkedEntity = tracker.spawnEntity(EntityTypes1_19_3.FALLING_BLOCK, storage.x(), storage.y(), storage.z(), value);\n            storage.setEntities(linkedEntity);\n\n            final PacketWrapper wrapper = PacketWrapper.create(ClientboundPackets1_19_3.SET_PASSENGERS, event.user());\n            wrapper.write(Types.VAR_INT, event.entityId()); // Entity id\n            wrapper.write(Types.VAR_INT_ARRAY_PRIMITIVE, new int[]{linkedEntity}); // Passenger entity ids\n            wrapper.send(Protocol1_19_4To1_19_3.class);\n        });\n        filter().type(EntityTypes1_19_4.ITEM_DISPLAY).index(22).handler((event, data) -> {\n            final Item value = data.value();\n\n            final PacketWrapper setEquipment = PacketWrapper.create(ClientboundPackets1_19_3.SET_EQUIPMENT, event.user());\n            setEquipment.write(Types.VAR_INT, event.entityId()); // Entity id\n            setEquipment.write(Types.BYTE, (byte) 5); // Slot - head\n            setEquipment.write(Types.ITEM1_13_2, value);\n\n            setEquipment.send(Protocol1_19_4To1_19_3.class);\n        });\n        filter().type(EntityTypes1_19_4.DISPLAY).handler((event, data) -> {\n            // Remove a large heap of display entity data\n            if (event.index() > 7) {\n                event.cancel();\n            }\n        });\n\n        filter().type(EntityTypes1_19_4.INTERACTION).cancel(8); // Width\n        filter().type(EntityTypes1_19_4.INTERACTION).cancel(9); // Height\n        filter().type(EntityTypes1_19_4.INTERACTION).cancel(10); // Response\n\n        filter().type(EntityTypes1_19_4.SNIFFER).cancel(17); // State\n        filter().type(EntityTypes1_19_4.SNIFFER).cancel(18); // Drop seed at tick\n\n        filter().type(EntityTypes1_19_4.ABSTRACT_HORSE).addIndex(18); // Owner UUID\n    }\n\n    @Override\n    public void onMappingDataLoaded() {\n        super.onMappingDataLoaded();\n        final EntityReplacement.EntityDataCreator displayDataCreator = storage -> {\n            storage.add(new EntityData(0, Types1_19_3.ENTITY_DATA_TYPES.byteType, (byte) 0x20)); // Invisible\n            storage.add(new EntityData(5, Types1_19_3.ENTITY_DATA_TYPES.booleanType, true)); // No gravity\n            storage.add(new EntityData(15, Types1_19_3.ENTITY_DATA_TYPES.byteType, (byte) (0x01 | 0x10))); // Small marker\n        };\n        mapEntityTypeWithData(EntityTypes1_19_4.TEXT_DISPLAY, EntityTypes1_19_4.ARMOR_STAND).spawnEntityData(displayDataCreator);\n        mapEntityTypeWithData(EntityTypes1_19_4.ITEM_DISPLAY, EntityTypes1_19_4.ARMOR_STAND).spawnEntityData(displayDataCreator);\n        mapEntityTypeWithData(EntityTypes1_19_4.BLOCK_DISPLAY, EntityTypes1_19_4.ARMOR_STAND).spawnEntityData(displayDataCreator);\n\n        mapEntityTypeWithData(EntityTypes1_19_4.INTERACTION, EntityTypes1_19_4.ARMOR_STAND).spawnEntityData(displayDataCreator); // Not much we can do about this one\n\n        mapEntityTypeWithData(EntityTypes1_19_4.SNIFFER, EntityTypes1_19_4.RAVAGER).jsonName();\n    }\n\n    @Override\n    public EntityType typeFromId(final int type) {\n        return EntityTypes1_19_4.getTypeFromId(type);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_19_4to1_19_3/storage/EntityTracker1_19_4.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_19_4to1_19_3.storage;\n\nimport com.viaversion.viabackwards.protocol.v1_19_4to1_19_3.Protocol1_19_4To1_19_3;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.data.entity.TrackedEntity;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_19_3;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_19_4;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.data.entity.EntityTrackerBase;\nimport com.viaversion.viaversion.libs.fastutil.ints.IntOpenHashSet;\nimport com.viaversion.viaversion.libs.fastutil.ints.IntSet;\nimport com.viaversion.viaversion.protocols.v1_19_1to1_19_3.packet.ClientboundPackets1_19_3;\nimport java.util.UUID;\nimport java.util.concurrent.ThreadLocalRandom;\n\npublic final class EntityTracker1_19_4 extends EntityTrackerBase {\n\n    private final IntSet generatedEntities = new IntOpenHashSet(); // Track entities spawned to prevent duplicated entity ids\n\n    public EntityTracker1_19_4(final UserConnection connection) {\n        super(connection, EntityTypes1_19_4.PLAYER);\n    }\n\n    public int spawnEntity(final EntityTypes1_19_3 entityType, final double x, final double y, final double z, final int data) {\n        final int entityId = nextEntityId();\n\n        final PacketWrapper addEntity = PacketWrapper.create(ClientboundPackets1_19_3.ADD_ENTITY, user());\n        addEntity.write(Types.VAR_INT, entityId); // Entity id\n        addEntity.write(Types.UUID, UUID.randomUUID()); // Entity UUID\n        addEntity.write(Types.VAR_INT, entityType.getId()); // Entity type\n        addEntity.write(Types.DOUBLE, x); // X\n        addEntity.write(Types.DOUBLE, y); // Y\n        addEntity.write(Types.DOUBLE, z); // Z\n        addEntity.write(Types.BYTE, (byte) 0); // Pitch\n        addEntity.write(Types.BYTE, (byte) 0); // Yaw\n        addEntity.write(Types.BYTE, (byte) 0); // Head yaw\n        addEntity.write(Types.VAR_INT, data); // Data\n        addEntity.write(Types.SHORT, (short) 0); // Velocity X\n        addEntity.write(Types.SHORT, (short) 0); // Velocity Y\n        addEntity.write(Types.SHORT, (short) 0); // Velocity Z\n\n        addEntity.send(Protocol1_19_4To1_19_3.class);\n\n        generatedEntities.add(entityId);\n        return entityId;\n    }\n\n    @Override\n    public void clearEntities() {\n        for (final int id : entities.keySet()) {\n            clearLinkedEntities(id);\n        }\n        super.clearEntities();\n    }\n\n    @Override\n    public void removeEntity(final int id) {\n        clearLinkedEntities(id);\n        super.removeEntity(id);\n    }\n\n    public void clearLinkedEntities(final int id) {\n        final LinkedEntityStorage storage = linkedEntityStorage(id);\n        if (storage != null && storage.entities() != null) {\n            storage.remove(user());\n            generatedEntities.remove(id);\n        }\n    }\n\n    public LinkedEntityStorage linkedEntityStorage(final int id) {\n        final TrackedEntity entity = entity(id);\n        if (entity != null && entity.hasData()) {\n            return entity.data().get(LinkedEntityStorage.class);\n        }\n        return null;\n    }\n\n    private int nextEntityId() {\n        final int entityId = -ThreadLocalRandom.current().nextInt(10_000);\n        if (generatedEntities.contains(entityId)) {\n            return nextEntityId();\n        }\n        return entityId;\n    }\n\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_19_4to1_19_3/storage/LinkedEntityStorage.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_19_4to1_19_3.storage;\n\nimport com.viaversion.viabackwards.api.entities.storage.EntityPositionStorage;\nimport com.viaversion.viabackwards.protocol.v1_19_4to1_19_3.Protocol1_19_4To1_19_3;\nimport com.viaversion.viaversion.api.connection.StorableObject;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.protocols.v1_19_1to1_19_3.packet.ClientboundPackets1_19_3;\n\npublic class LinkedEntityStorage extends EntityPositionStorage implements StorableObject {\n\n    private int[] entities;\n\n    public int[] entities() {\n        return entities;\n    }\n\n    public void setEntities(final int... entities) {\n        this.entities = entities;\n    }\n\n    public void remove(final UserConnection connection) {\n        final PacketWrapper wrapper = PacketWrapper.create(ClientboundPackets1_19_3.REMOVE_ENTITIES, connection);\n        wrapper.write(Types.VAR_INT_ARRAY_PRIMITIVE, entities);\n\n        wrapper.send(Protocol1_19_4To1_19_3.class);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_19to1_18_2/Protocol1_19To1_18_2.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_19to1_18_2;\n\nimport com.google.common.primitives.Longs;\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viabackwards.api.rewriters.SoundRewriter;\nimport com.viaversion.viabackwards.api.rewriters.text.JsonNBTComponentRewriter;\nimport com.viaversion.viabackwards.protocol.v1_19_1to1_19.Protocol1_19_1To1_19;\nimport com.viaversion.viabackwards.protocol.v1_19to1_18_2.data.BackwardsMappingData1_19;\nimport com.viaversion.viabackwards.protocol.v1_19to1_18_2.rewriter.BlockItemPacketRewriter1_19;\nimport com.viaversion.viabackwards.protocol.v1_19to1_18_2.rewriter.CommandRewriter1_19;\nimport com.viaversion.viabackwards.protocol.v1_19to1_18_2.rewriter.EntityPacketRewriter1_19;\nimport com.viaversion.viabackwards.protocol.v1_19to1_18_2.storage.DimensionRegistryStorage;\nimport com.viaversion.viabackwards.protocol.v1_19to1_18_2.storage.EntityTracker1_19;\nimport com.viaversion.viabackwards.protocol.v1_19to1_18_2.storage.NonceStorage;\nimport com.viaversion.viaversion.api.Via;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.RegistryType;\nimport com.viaversion.viaversion.api.minecraft.signature.SignableCommandArgumentsProvider;\nimport com.viaversion.viaversion.api.minecraft.signature.model.DecoratableMessage;\nimport com.viaversion.viaversion.api.minecraft.signature.model.MessageMetadata;\nimport com.viaversion.viaversion.api.minecraft.signature.storage.ChatSession1_19_0;\nimport com.viaversion.viaversion.api.protocol.packet.State;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.chunk.ChunkType1_18;\nimport com.viaversion.viaversion.libs.gson.JsonElement;\nimport com.viaversion.viaversion.protocols.base.ClientboundLoginPackets;\nimport com.viaversion.viaversion.protocols.base.ServerboundLoginPackets;\nimport com.viaversion.viaversion.protocols.v1_16_4to1_17.packet.ServerboundPackets1_17;\nimport com.viaversion.viaversion.protocols.v1_17_1to1_18.packet.ClientboundPackets1_18;\nimport com.viaversion.viaversion.protocols.v1_18_2to1_19.packet.ClientboundPackets1_19;\nimport com.viaversion.viaversion.protocols.v1_18_2to1_19.packet.ServerboundPackets1_19;\nimport com.viaversion.viaversion.protocols.v1_19to1_19_1.Protocol1_19To1_19_1;\nimport com.viaversion.viaversion.protocols.v1_19to1_19_1.data.ChatDecorationResult;\nimport com.viaversion.viaversion.rewriter.BlockRewriter;\nimport com.viaversion.viaversion.rewriter.CommandRewriter;\nimport com.viaversion.viaversion.rewriter.ParticleRewriter;\nimport com.viaversion.viaversion.rewriter.TagRewriter;\nimport com.viaversion.viaversion.rewriter.text.ComponentRewriterBase;\nimport com.viaversion.viaversion.util.Pair;\nimport java.security.SignatureException;\nimport java.time.Instant;\nimport java.util.List;\nimport java.util.UUID;\nimport java.util.concurrent.ThreadLocalRandom;\n\npublic final class Protocol1_19To1_18_2 extends BackwardsProtocol<ClientboundPackets1_19, ClientboundPackets1_18, ServerboundPackets1_19, ServerboundPackets1_17> {\n\n    public static final BackwardsMappingData1_19 MAPPINGS = new BackwardsMappingData1_19();\n    private static final UUID ZERO_UUID = new UUID(0, 0);\n    private static final byte[] EMPTY_BYTES = new byte[0];\n    private final EntityPacketRewriter1_19 entityRewriter = new EntityPacketRewriter1_19(this);\n    private final BlockItemPacketRewriter1_19 blockItemPackets = new BlockItemPacketRewriter1_19(this);\n    private final ParticleRewriter<ClientboundPackets1_19> particleRewriter = new ParticleRewriter<>(this);\n    private final JsonNBTComponentRewriter<ClientboundPackets1_19> translatableRewriter = new JsonNBTComponentRewriter<>(this, ComponentRewriterBase.ReadType.JSON);\n    private final TagRewriter<ClientboundPackets1_19> tagRewriter = new TagRewriter<>(this);\n    private final BlockRewriter<ClientboundPackets1_19> blockRewriter = BlockRewriter.for1_18(this, ChunkType1_18::new);\n\n    public Protocol1_19To1_18_2() {\n        super(ClientboundPackets1_19.class, ClientboundPackets1_18.class, ServerboundPackets1_19.class, ServerboundPackets1_17.class);\n    }\n\n    @Override\n    protected void registerPackets() {\n        super.registerPackets();\n\n        final SoundRewriter<ClientboundPackets1_19> soundRewriter = new SoundRewriter<>(this);\n        replaceClientbound(ClientboundPackets1_19.SOUND, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT); // Sound id\n                map(Types.VAR_INT); // Source\n                map(Types.INT); // X\n                map(Types.INT); // Y\n                map(Types.INT); // Z\n                map(Types.FLOAT); // Volume\n                map(Types.FLOAT); // Pitch\n                read(Types.LONG); // Seed\n                handler(soundRewriter.getSoundHandler());\n            }\n        });\n        replaceClientbound(ClientboundPackets1_19.SOUND_ENTITY, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT); // Sound id\n                map(Types.VAR_INT); // Source\n                map(Types.VAR_INT); // Entity id\n                map(Types.FLOAT); // Volume\n                map(Types.FLOAT); // Pitch\n                read(Types.LONG); // Seed\n                handler(soundRewriter.getSoundHandler());\n            }\n        });\n        replaceClientbound(ClientboundPackets1_19.CUSTOM_SOUND, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.STRING); // Sound name\n                map(Types.VAR_INT); // Source\n                map(Types.INT); // X\n                map(Types.INT); // Y\n                map(Types.INT); // Z\n                map(Types.FLOAT); // Volume\n                map(Types.FLOAT); // Pitch\n                read(Types.LONG); // Seed\n                handler(soundRewriter.getNamedSoundHandler());\n            }\n        });\n\n        tagRewriter.removeTags(\"minecraft:banner_pattern\");\n        tagRewriter.removeTags(\"minecraft:instrument\");\n        tagRewriter.removeTags(\"minecraft:cat_variant\");\n        tagRewriter.removeTags(\"minecraft:painting_variant\");\n        tagRewriter.addEmptyTag(RegistryType.BLOCK, \"minecraft:polar_bears_spawnable_on_in_frozen_ocean\");\n        tagRewriter.renameTag(RegistryType.BLOCK, \"minecraft:wool_carpets\", \"minecraft:carpets\");\n        tagRewriter.renameTag(RegistryType.ITEM, \"minecraft:wool_carpets\", \"minecraft:carpets\");\n        tagRewriter.addEmptyTag(RegistryType.ITEM, \"minecraft:occludes_vibration_signals\");\n\n        final CommandRewriter<ClientboundPackets1_19> commandRewriter = new CommandRewriter1_19(this);\n        replaceClientbound(ClientboundPackets1_19.COMMANDS, wrapper -> {\n            final int size = wrapper.passthrough(Types.VAR_INT);\n            for (int i = 0; i < size; i++) {\n                final byte flags = wrapper.passthrough(Types.BYTE);\n                wrapper.passthrough(Types.VAR_INT_ARRAY_PRIMITIVE); // Children indices\n                if ((flags & 0x08) != 0) {\n                    wrapper.passthrough(Types.VAR_INT); // Redirect node index\n                }\n\n                final int nodeType = flags & 0x03;\n                if (nodeType == 1 || nodeType == 2) { // Literal/argument node\n                    wrapper.passthrough(Types.STRING); // Name\n                }\n\n                if (nodeType == 2) { // Argument node\n                    final int argumentTypeId = wrapper.read(Types.VAR_INT);\n                    String argumentType = MAPPINGS.getArgumentTypeMappings().identifier(argumentTypeId);\n                    if (argumentType == null) {\n                        getLogger().warning(\"Unknown command argument type id: \" + argumentTypeId);\n                        argumentType = \"minecraft:no\";\n                    }\n\n                    wrapper.write(Types.STRING, commandRewriter.handleArgumentType(argumentType));\n                    commandRewriter.handleArgument(wrapper, argumentType);\n\n                    if ((flags & 0x10) != 0) {\n                        wrapper.passthrough(Types.STRING); // Suggestion type\n                    }\n                }\n            }\n\n            wrapper.passthrough(Types.VAR_INT); // Root node index\n        });\n\n        cancelClientbound(ClientboundPackets1_19.SERVER_DATA);\n        cancelClientbound(ClientboundPackets1_19.CHAT_PREVIEW);\n        cancelClientbound(ClientboundPackets1_19.SET_DISPLAY_CHAT_PREVIEW);\n        registerClientbound(ClientboundPackets1_19.PLAYER_CHAT, ClientboundPackets1_18.CHAT, new PacketHandlers() {\n            @Override\n            public void register() {\n                handler(wrapper -> {\n                    final JsonElement signedContent = wrapper.read(Types.COMPONENT);\n                    final JsonElement unsignedContent = wrapper.read(Types.OPTIONAL_COMPONENT);\n                    final int chatTypeId = wrapper.read(Types.VAR_INT);\n                    final UUID sender = wrapper.read(Types.UUID);\n                    final JsonElement senderName = wrapper.read(Types.COMPONENT);\n                    final JsonElement teamName = wrapper.read(Types.OPTIONAL_COMPONENT);\n\n                    final CompoundTag chatType = wrapper.user().get(DimensionRegistryStorage.class).chatType(chatTypeId);\n                    final ChatDecorationResult decorationResult = Protocol1_19To1_19_1.decorateChatMessage(chatType, chatTypeId, senderName, teamName, unsignedContent != null ? unsignedContent : signedContent);\n                    if (decorationResult == null) {\n                        wrapper.cancel();\n                        return;\n                    }\n\n                    translatableRewriter.processText(wrapper.user(), decorationResult.content());\n                    wrapper.write(Types.COMPONENT, decorationResult.content());\n                    wrapper.write(Types.BYTE, decorationResult.overlay() ? (byte) 2 : 1);\n                    wrapper.write(Types.UUID, sender);\n                });\n                read(Types.LONG); // Timestamp\n                read(Types.LONG); // Salt\n                read(Types.BYTE_ARRAY_PRIMITIVE); // Signature\n            }\n        });\n\n        registerClientbound(ClientboundPackets1_19.SYSTEM_CHAT, ClientboundPackets1_18.CHAT, new PacketHandlers() {\n            @Override\n            public void register() {\n                handler(wrapper -> {\n                    final JsonElement content = wrapper.passthrough(Types.COMPONENT);\n                    translatableRewriter.processText(wrapper.user(), content);\n\n                    // Screw everything that isn't a system or game info type (which would only happen on funny 1.19.0 servers)\n                    final int typeId = wrapper.read(Types.VAR_INT);\n                    wrapper.write(Types.BYTE, typeId == Protocol1_19_1To1_19.GAME_INFO_ID ? (byte) 2 : (byte) 0);\n                });\n                create(Types.UUID, ZERO_UUID); // Sender\n            }\n        }, true);\n\n        registerServerbound(ServerboundPackets1_17.CHAT, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.STRING); // Message\n                handler(wrapper -> {\n                    final ChatSession1_19_0 chatSession = wrapper.user().get(ChatSession1_19_0.class);\n\n                    final UUID sender = wrapper.user().getProtocolInfo().getUuid();\n                    final Instant timestamp = Instant.now();\n                    final long salt = ThreadLocalRandom.current().nextLong();\n\n                    wrapper.write(Types.LONG, timestamp.toEpochMilli()); // Timestamp\n                    wrapper.write(Types.LONG, chatSession != null ? salt : 0L); // Salt\n\n                    final String message = wrapper.get(Types.STRING, 0);\n                    if (!message.isEmpty() && message.charAt(0) == '/') {\n                        final String command = message.substring(1);\n\n                        wrapper.setPacketType(ServerboundPackets1_19.CHAT_COMMAND);\n                        wrapper.set(Types.STRING, 0, command);\n\n                        final SignableCommandArgumentsProvider argumentsProvider = Via.getManager().getProviders().get(SignableCommandArgumentsProvider.class);\n                        if (chatSession != null && argumentsProvider != null) {\n                            final MessageMetadata metadata = new MessageMetadata(sender, timestamp, salt);\n\n                            final List<Pair<String, String>> arguments = argumentsProvider.getSignableArguments(command);\n                            wrapper.write(Types.VAR_INT, arguments.size());\n                            for (final Pair<String, String> argument : arguments) {\n                                final byte[] signature;\n                                try {\n                                    signature = chatSession.signChatMessage(metadata, new DecoratableMessage(argument.value()));\n                                } catch (final SignatureException e) {\n                                    throw new RuntimeException(e);\n                                }\n\n                                wrapper.write(Types.STRING, argument.key());\n                                wrapper.write(Types.BYTE_ARRAY_PRIMITIVE, signature);\n                            }\n                        } else {\n                            wrapper.write(Types.VAR_INT, 0); // No signatures\n                        }\n                    } else {\n                        if (chatSession != null) {\n                            final MessageMetadata metadata = new MessageMetadata(sender, timestamp, salt);\n                            final DecoratableMessage decoratableMessage = new DecoratableMessage(message);\n                            final byte[] signature;\n                            try {\n                                signature = chatSession.signChatMessage(metadata, decoratableMessage);\n                            } catch (final SignatureException e) {\n                                throw new RuntimeException(e);\n                            }\n\n                            wrapper.write(Types.BYTE_ARRAY_PRIMITIVE, signature); // Signature\n                        } else {\n                            wrapper.write(Types.BYTE_ARRAY_PRIMITIVE, EMPTY_BYTES); // Signature\n                        }\n                    }\n\n                    wrapper.write(Types.BOOLEAN, false); // No signed preview\n                });\n            }\n        });\n\n        // Login changes\n        registerClientbound(State.LOGIN, ClientboundLoginPackets.LOGIN_FINISHED, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.UUID); // UUID\n                map(Types.STRING); // Name\n                read(Types.PROFILE_PROPERTY_ARRAY);\n            }\n        });\n\n        registerClientbound(State.LOGIN, ClientboundLoginPackets.HELLO, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.STRING); // Server id\n                map(Types.BYTE_ARRAY_PRIMITIVE); // Public key\n                handler(wrapper -> {\n                    if (wrapper.user().has(ChatSession1_19_0.class)) {\n                        wrapper.user().put(new NonceStorage(wrapper.passthrough(Types.BYTE_ARRAY_PRIMITIVE))); // Nonce\n                    }\n                });\n            }\n        });\n        registerServerbound(State.LOGIN, ServerboundLoginPackets.HELLO, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.STRING); // Name\n                handler(wrapper -> {\n                    final ChatSession1_19_0 chatSession = wrapper.user().get(ChatSession1_19_0.class);\n                    wrapper.write(Types.OPTIONAL_PROFILE_KEY, chatSession == null ? null : chatSession.getProfileKey()); // Profile Key\n                });\n            }\n        });\n\n        registerServerbound(State.LOGIN, ServerboundLoginPackets.ENCRYPTION_KEY, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.BYTE_ARRAY_PRIMITIVE); // Public key\n                handler(wrapper -> {\n                    final ChatSession1_19_0 chatSession = wrapper.user().get(ChatSession1_19_0.class);\n\n                    final byte[] verifyToken = wrapper.read(Types.BYTE_ARRAY_PRIMITIVE); // Verify token\n                    wrapper.write(Types.BOOLEAN, chatSession == null); // Is nonce\n                    if (chatSession != null) {\n                        final long salt = ThreadLocalRandom.current().nextLong();\n                        final byte[] signature;\n                        try {\n                            signature = chatSession.sign(signer -> {\n                                signer.accept(wrapper.user().remove(NonceStorage.class).nonce());\n                                signer.accept(Longs.toByteArray(salt));\n                            });\n                        } catch (final SignatureException e) {\n                            throw new RuntimeException(e);\n                        }\n                        wrapper.write(Types.LONG, salt); // Salt\n                        wrapper.write(Types.BYTE_ARRAY_PRIMITIVE, signature); // Signature\n                    } else {\n                        wrapper.write(Types.BYTE_ARRAY_PRIMITIVE, verifyToken); // Nonce\n                    }\n                });\n            }\n        });\n    }\n\n    @Override\n    public void init(final UserConnection user) {\n        user.put(new DimensionRegistryStorage());\n        addEntityTracker(user, new EntityTracker1_19(user));\n    }\n\n    @Override\n    public BackwardsMappingData1_19 getMappingData() {\n        return MAPPINGS;\n    }\n\n    @Override\n    public JsonNBTComponentRewriter<ClientboundPackets1_19> getComponentRewriter() {\n        return translatableRewriter;\n    }\n\n    @Override\n    public EntityPacketRewriter1_19 getEntityRewriter() {\n        return entityRewriter;\n    }\n\n    @Override\n    public BlockItemPacketRewriter1_19 getItemRewriter() {\n        return blockItemPackets;\n    }\n\n    @Override\n    public BlockRewriter<ClientboundPackets1_19> getBlockRewriter() {\n        return blockRewriter;\n    }\n\n    @Override\n    public ParticleRewriter<ClientboundPackets1_19> getParticleRewriter() {\n        return particleRewriter;\n    }\n\n    @Override\n    public TagRewriter<ClientboundPackets1_19> getTagRewriter() {\n        return tagRewriter;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_19to1_18_2/data/BackwardsMappingData1_19.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_19to1_18_2.data;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.ListTag;\nimport com.viaversion.nbt.tag.NumberTag;\nimport com.viaversion.viabackwards.ViaBackwards;\nimport com.viaversion.viabackwards.api.data.BackwardsMappingData;\nimport com.viaversion.viabackwards.api.data.BackwardsMappingDataLoader;\nimport com.viaversion.viaversion.libs.fastutil.ints.Int2ObjectMap;\nimport com.viaversion.viaversion.libs.fastutil.ints.Int2ObjectOpenHashMap;\nimport com.viaversion.viaversion.protocols.v1_18_2to1_19.Protocol1_18_2To1_19;\nimport org.checkerframework.checker.nullness.qual.Nullable;\n\npublic final class BackwardsMappingData1_19 extends BackwardsMappingData {\n\n    private final Int2ObjectMap<CompoundTag> defaultChatTypes = new Int2ObjectOpenHashMap<>();\n\n    public BackwardsMappingData1_19() {\n        super(\"1.19\", \"1.18\", Protocol1_18_2To1_19.class);\n    }\n\n    @Override\n    protected void loadExtras(final CompoundTag data) {\n        if (ViaBackwards.getConfig().sculkShriekerToCryingObsidian()) {\n            for (int i = 18900; i <= 18907; i++) {\n                blockStateMappings.setNewId(i, 16082);\n            }\n            itemMappings.setNewId(329, 1065);\n        }\n\n        super.loadExtras(data);\n\n        final ListTag<CompoundTag> chatTypes = BackwardsMappingDataLoader.INSTANCE.loadNBT(\"chat-types-1.19.1.nbt\").getListTag(\"values\", CompoundTag.class);\n        for (final CompoundTag chatType : chatTypes) {\n            final NumberTag idTag = chatType.getNumberTag(\"id\");\n            defaultChatTypes.put(idTag.asInt(), chatType);\n        }\n    }\n\n    public @Nullable CompoundTag chatType(final int id) {\n        return defaultChatTypes.get(id);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_19to1_18_2/rewriter/BlockItemPacketRewriter1_19.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_19to1_18_2.rewriter;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.viabackwards.api.rewriters.BackwardsItemRewriter;\nimport com.viaversion.viabackwards.api.rewriters.EnchantmentRewriter;\nimport com.viaversion.viabackwards.protocol.v1_19to1_18_2.Protocol1_19To1_18_2;\nimport com.viaversion.viabackwards.protocol.v1_19to1_18_2.storage.LastDeathPosition;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.data.ParticleMappings;\nimport com.viaversion.viaversion.api.data.entity.EntityTracker;\nimport com.viaversion.viaversion.api.minecraft.GlobalBlockPosition;\nimport com.viaversion.viaversion.api.minecraft.chunks.Chunk;\nimport com.viaversion.viaversion.api.minecraft.chunks.ChunkSection;\nimport com.viaversion.viaversion.api.minecraft.chunks.DataPalette;\nimport com.viaversion.viaversion.api.minecraft.chunks.PaletteType;\nimport com.viaversion.viaversion.api.minecraft.item.Item;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.chunk.ChunkType1_18;\nimport com.viaversion.viaversion.protocols.v1_16_4to1_17.packet.ServerboundPackets1_17;\nimport com.viaversion.viaversion.protocols.v1_18_2to1_19.packet.ClientboundPackets1_19;\nimport com.viaversion.viaversion.rewriter.RecipeRewriter;\nimport com.viaversion.viaversion.util.MathUtil;\n\npublic final class BlockItemPacketRewriter1_19 extends BackwardsItemRewriter<ClientboundPackets1_19, ServerboundPackets1_17, Protocol1_19To1_18_2> {\n\n    private final EnchantmentRewriter enchantmentRewriter = new EnchantmentRewriter(this);\n\n    public BlockItemPacketRewriter1_19(final Protocol1_19To1_18_2 protocol) {\n        super(protocol, Types.ITEM1_13_2, Types.ITEM1_13_2_ARRAY);\n    }\n\n    @Override\n    protected void registerPackets() {\n        new RecipeRewriter<>(protocol).register(ClientboundPackets1_19.UPDATE_RECIPES);\n\n        protocol.replaceClientbound(ClientboundPackets1_19.MERCHANT_OFFERS, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT); // Container id\n                handler(wrapper -> {\n                    final int size = wrapper.read(Types.VAR_INT);\n                    wrapper.write(Types.UNSIGNED_BYTE, (short) size);\n                    for (int i = 0; i < size; i++) {\n                        passthroughClientboundItem(wrapper); // First item\n                        passthroughClientboundItem(wrapper); // Result\n\n                        Item secondItem = wrapper.read(Types.ITEM1_13_2);\n                        if (secondItem != null) {\n                            secondItem = handleItemToClient(wrapper.user(), secondItem);\n                            wrapper.write(Types.BOOLEAN, true);\n                            wrapper.write(Types.ITEM1_13_2, secondItem);\n                        } else {\n                            wrapper.write(Types.BOOLEAN, false);\n                        }\n\n                        wrapper.passthrough(Types.BOOLEAN); // Out of stock\n                        wrapper.passthrough(Types.INT); // Uses\n                        wrapper.passthrough(Types.INT); // Max uses\n                        wrapper.passthrough(Types.INT); // Xp\n                        wrapper.passthrough(Types.INT); // Special price diff\n                        wrapper.passthrough(Types.FLOAT); // Price multiplier\n                        wrapper.passthrough(Types.INT); //Demand\n                    }\n                });\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_19.BLOCK_CHANGED_ACK, null, new PacketHandlers() {\n            @Override\n            public void register() {\n                read(Types.VAR_INT); // Sequence\n                handler(PacketWrapper::cancel); // This is fine:tm:\n            }\n        });\n\n        protocol.replaceClientbound(ClientboundPackets1_19.LEVEL_PARTICLES, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT, Types.INT); // Particle id\n                map(Types.BOOLEAN); // Override limiter\n                map(Types.DOUBLE); // X\n                map(Types.DOUBLE); // Y\n                map(Types.DOUBLE); // Z\n                map(Types.FLOAT); // Offset X\n                map(Types.FLOAT); // Offset Y\n                map(Types.FLOAT); // Offset Z\n                map(Types.FLOAT); // Max speed\n                map(Types.INT); // Particle Count\n                handler(wrapper -> {\n                    final int id = wrapper.get(Types.INT, 0);\n                    final ParticleMappings particleMappings = protocol.getMappingData().getParticleMappings();\n                    if (id == particleMappings.id(\"sculk_charge\")) {\n                        //TODO\n                        wrapper.set(Types.INT, 0, -1);\n                        wrapper.cancel();\n                    } else if (id == particleMappings.id(\"shriek\")) {\n                        //TODO\n                        wrapper.set(Types.INT, 0, -1);\n                        wrapper.cancel();\n                    } else if (id == particleMappings.id(\"vibration\")) {\n                        // Can't do without the position\n                        wrapper.set(Types.INT, 0, -1);\n                        wrapper.cancel();\n                    }\n                });\n                handler(protocol.getParticleRewriter().levelParticlesHandler1_13(Types.INT));\n            }\n        });\n\n        // The server does nothing but track the sequence, so we can just set it as 0\n        protocol.registerServerbound(ServerboundPackets1_17.PLAYER_ACTION, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT); // Action\n                map(Types.BLOCK_POSITION1_14); // Block position\n                map(Types.UNSIGNED_BYTE); // Direction\n                create(Types.VAR_INT, 0); // Sequence\n            }\n        });\n        protocol.registerServerbound(ServerboundPackets1_17.USE_ITEM_ON, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT); // Hand\n                map(Types.BLOCK_POSITION1_14); // Block position\n                map(Types.VAR_INT); // Direction\n                map(Types.FLOAT); // X\n                map(Types.FLOAT); // Y\n                map(Types.FLOAT); // Z\n                map(Types.BOOLEAN); // Inside\n                create(Types.VAR_INT, 0); // Sequence\n            }\n        });\n        protocol.registerServerbound(ServerboundPackets1_17.USE_ITEM, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT); // Hand\n                create(Types.VAR_INT, 0); // Sequence\n            }\n        });\n\n        protocol.registerServerbound(ServerboundPackets1_17.SET_BEACON, wrapper -> {\n            final int primaryEffect = wrapper.read(Types.VAR_INT);\n            if (primaryEffect > 0) {\n                wrapper.write(Types.BOOLEAN, true);\n                wrapper.write(Types.VAR_INT, primaryEffect);\n            } else {\n                wrapper.write(Types.BOOLEAN, false);\n            }\n\n            final int secondaryEffect = wrapper.read(Types.VAR_INT);\n            if (secondaryEffect > 0) {\n                wrapper.write(Types.BOOLEAN, true);\n                wrapper.write(Types.VAR_INT, secondaryEffect);\n            } else {\n                wrapper.write(Types.BOOLEAN, false);\n            }\n        });\n    }\n\n    @Override\n    protected void registerRewrites() {\n        enchantmentRewriter.registerEnchantment(\"minecraft:swift_sneak\", \"§7Swift Sneak\");\n    }\n\n    @Override\n    public Item handleItemToClient(final UserConnection connection, Item item) {\n        if (item == null) return null;\n\n        final int identifier = item.identifier();\n        item = super.handleItemToClient(connection, item);\n\n        if (identifier != 834) {\n            return item;\n        }\n        final LastDeathPosition lastDeathPosition = connection.get(LastDeathPosition.class);\n        if (lastDeathPosition == null) {\n            return item;\n        }\n        final GlobalBlockPosition position = lastDeathPosition.position();\n\n        final CompoundTag lodestonePosTag = new CompoundTag();\n        item.tag().putBoolean(nbtTagName(), true);\n        item.tag().put(\"LodestonePos\", lodestonePosTag);\n        item.tag().putString(\"LodestoneDimension\", position.dimension());\n\n        lodestonePosTag.putInt(\"X\", position.x());\n        lodestonePosTag.putInt(\"Y\", position.y());\n        lodestonePosTag.putInt(\"Z\", position.z());\n\n        enchantmentRewriter.handleToClient(item);\n        return item;\n    }\n\n    @Override\n    public Item handleItemToServer(final UserConnection connection, Item item) {\n        if (item == null) return null;\n\n        item = super.handleItemToServer(connection, item);\n\n        CompoundTag tag = item.tag();\n        if (item.identifier() == 834 && tag != null) {\n            if (tag.contains(nbtTagName())) {\n                tag.remove(nbtTagName());\n                tag.remove(\"LodestonePos\");\n                tag.remove(\"LodestoneDimension\");\n            }\n            if (tag.isEmpty()) {\n                item.setTag(null);\n            }\n        }\n\n        enchantmentRewriter.handleToServer(item);\n        return item;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_19to1_18_2/rewriter/CommandRewriter1_19.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_19to1_18_2.rewriter;\n\nimport com.viaversion.viabackwards.protocol.v1_19to1_18_2.Protocol1_19To1_18_2;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.protocols.v1_18_2to1_19.packet.ClientboundPackets1_19;\nimport com.viaversion.viaversion.rewriter.CommandRewriter;\n\npublic final class CommandRewriter1_19 extends CommandRewriter<ClientboundPackets1_19> {\n\n    public CommandRewriter1_19(Protocol1_19To1_18_2 protocol) {\n        super(protocol);\n        this.parserHandlers.put(\"minecraft:template_mirror\", wrapper -> wrapper.write(Types.VAR_INT, 0));\n        this.parserHandlers.put(\"minecraft:template_rotation\", wrapper -> wrapper.write(Types.VAR_INT, 0));\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_19to1_18_2/rewriter/EntityPacketRewriter1_19.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_19to1_18_2.rewriter;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.ListTag;\nimport com.viaversion.nbt.tag.NumberTag;\nimport com.viaversion.viabackwards.ViaBackwards;\nimport com.viaversion.viabackwards.api.rewriters.EntityRewriter;\nimport com.viaversion.viabackwards.protocol.v1_19to1_18_2.Protocol1_19To1_18_2;\nimport com.viaversion.viabackwards.protocol.v1_19to1_18_2.storage.DimensionRegistryStorage;\nimport com.viaversion.viabackwards.protocol.v1_19to1_18_2.storage.EntityTracker1_19;\nimport com.viaversion.viabackwards.protocol.v1_19to1_18_2.storage.LastDeathPosition;\nimport com.viaversion.viabackwards.protocol.v1_19to1_18_2.storage.StoredPainting;\nimport com.viaversion.viaversion.api.data.ParticleMappings;\nimport com.viaversion.viaversion.api.data.entity.StoredEntityData;\nimport com.viaversion.viaversion.api.minecraft.BlockPosition;\nimport com.viaversion.viaversion.api.minecraft.GlobalBlockPosition;\nimport com.viaversion.viaversion.api.minecraft.Particle;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityType;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_19;\nimport com.viaversion.viaversion.api.minecraft.entitydata.EntityDataType;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.version.Types1_18;\nimport com.viaversion.viaversion.api.type.types.version.Types1_19;\nimport com.viaversion.viaversion.libs.fastutil.ints.IntOpenHashSet;\nimport com.viaversion.viaversion.libs.fastutil.ints.IntSet;\nimport com.viaversion.viaversion.protocols.v1_17_1to1_18.packet.ClientboundPackets1_18;\nimport com.viaversion.viaversion.protocols.v1_18_2to1_19.packet.ClientboundPackets1_19;\nimport com.viaversion.viaversion.util.Key;\nimport com.viaversion.viaversion.util.TagUtil;\n\npublic final class EntityPacketRewriter1_19 extends EntityRewriter<ClientboundPackets1_19, Protocol1_19To1_18_2> {\n\n    private static final IntSet WIDE_PAINTINGS = IntOpenHashSet.of(\n        7, 8, 9, 10, 11,\n        14, 15, 16, 17, 18, 19, 20,\n        21, 22, 23, 24, 25\n    );\n\n    public EntityPacketRewriter1_19(final Protocol1_19To1_18_2 protocol) {\n        super(protocol);\n    }\n\n    @Override\n    protected void registerPackets() {\n        registerTracker(ClientboundPackets1_19.ADD_EXPERIENCE_ORB, EntityTypes1_19.EXPERIENCE_ORB);\n        registerTracker(ClientboundPackets1_19.ADD_PLAYER, EntityTypes1_19.PLAYER);\n        registerSetEntityData(ClientboundPackets1_19.SET_ENTITY_DATA, Types1_19.ENTITY_DATA_LIST, Types1_18.ENTITY_DATA_LIST);\n\n        protocol.replaceClientbound(ClientboundPackets1_19.ADD_ENTITY, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT); // Entity id\n                map(Types.UUID); // Entity UUID\n                map(Types.VAR_INT); // Entity Type\n                map(Types.DOUBLE); // X\n                map(Types.DOUBLE); // Y\n                map(Types.DOUBLE); // Z\n                map(Types.BYTE); // Pitch\n                map(Types.BYTE); // Yaw\n                handler(wrapper -> {\n                    final byte headYaw = wrapper.read(Types.BYTE);\n                    int data = wrapper.read(Types.VAR_INT);\n                    final EntityType entityType = trackAndMapEntity(wrapper);\n                    if (entityType.isOrHasParent(EntityTypes1_19.LIVING_ENTITY)) {\n                        wrapper.write(Types.BYTE, headYaw);\n\n                        // Switch pitch and yaw position\n                        final byte pitch = wrapper.get(Types.BYTE, 0);\n                        final byte yaw = wrapper.get(Types.BYTE, 1);\n                        wrapper.set(Types.BYTE, 0, yaw);\n                        wrapper.set(Types.BYTE, 1, pitch);\n\n                        wrapper.setPacketType(ClientboundPackets1_18.ADD_MOB);\n                        return;\n                    } else if (entityType == EntityTypes1_19.PAINTING) {\n                        wrapper.cancel();\n                        // The entity has been tracked, now we wait for the entity data packet\n                        final int entityId = wrapper.get(Types.VAR_INT, 0);\n                        final StoredEntityData entityData = tracker(wrapper.user()).entityData(entityId);\n                        final int x = wrapper.get(Types.DOUBLE, 0).intValue();\n                        final int y = wrapper.get(Types.DOUBLE, 1).intValue();\n                        final int z = wrapper.get(Types.DOUBLE, 2).intValue();\n                        final BlockPosition position = new BlockPosition(x, y, z);\n                        entityData.put(new StoredPainting(entityId, wrapper.get(Types.UUID, 0), position, data));\n                        return;\n                    }\n\n                    if (entityType == EntityTypes1_19.FALLING_BLOCK) {\n                        data = protocol.getMappingData().getNewBlockStateId(data);\n                    }\n                    wrapper.write(Types.INT, data);\n                });\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_19.TELEPORT_ENTITY, wrapper -> {\n            final int entityId = wrapper.passthrough(Types.VAR_INT);\n            if (tracker(wrapper.user()).entityType(entityId) != EntityTypes1_19.PAINTING) {\n                return;\n            }\n\n            final double x = wrapper.read(Types.DOUBLE);\n            final double y = wrapper.read(Types.DOUBLE);\n            final double z = wrapper.read(Types.DOUBLE);\n\n            final StoredEntityData entityData = tracker(wrapper.user()).entityDataIfPresent(entityId);\n            final StoredPainting storedPainting = entityData != null ? entityData.get(StoredPainting.class) : null;\n            // Presumably there is a more correct way of fixing this? Only north and east looking paintings with a width of >1 seem to be extra special\n            if (storedPainting != null && (storedPainting.direction() == 2 || storedPainting.direction() == 3)\n                && WIDE_PAINTINGS.contains(storedPainting.type())) {\n                wrapper.write(Types.DOUBLE, storedPainting.direction() == 2 ? x : x + 1);\n                wrapper.write(Types.DOUBLE, y + 1);\n                wrapper.write(Types.DOUBLE, storedPainting.direction() == 3 ? z : z + 1);\n            } else {\n                wrapper.write(Types.DOUBLE, x + 1);\n                wrapper.write(Types.DOUBLE, y + 1);\n                wrapper.write(Types.DOUBLE, z + 1);\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_19.UPDATE_MOB_EFFECT, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT); // Entity id\n                map(Types.VAR_INT); // Effect id\n                map(Types.BYTE); // Amplifier\n                map(Types.VAR_INT); // Duration\n                map(Types.BYTE); // Flags\n                handler(wrapper -> {\n                    // Remove factor data\n                    wrapper.read(Types.OPTIONAL_NAMED_COMPOUND_TAG);\n\n                    if (!ViaBackwards.getConfig().mapDarknessEffect()) {\n                        return;\n                    }\n\n                    final EntityTracker1_19 tracker = tracker(wrapper.user());\n\n                    final int entityId = wrapper.get(Types.VAR_INT, 0);\n                    final int effectId = wrapper.get(Types.VAR_INT, 1);\n                    if (effectId == 33) { // Newly added darkness, rewrite to blindness\n                        tracker.getAffectedByDarkness().add(entityId);\n                        wrapper.set(Types.VAR_INT, 1, 15);\n                    } else if (effectId == 15) { // Track actual blindness effect for removal later\n                        tracker.getAffectedByBlindness().add(entityId);\n                    }\n                });\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_19.REMOVE_MOB_EFFECT, new PacketHandlers() {\n            @Override\n            protected void register() {\n                map(Types.VAR_INT); // Entity id\n                map(Types.VAR_INT); // Effect id\n                handler(wrapper -> {\n                    if (!ViaBackwards.getConfig().mapDarknessEffect()) {\n                        return;\n                    }\n\n                    final int entityId = wrapper.get(Types.VAR_INT, 0);\n                    final int effectId = wrapper.get(Types.VAR_INT, 1);\n\n                    final EntityTracker1_19 tracker = tracker(wrapper.user());\n                    if (effectId == 33) { // Remove darkness and the fake blindness effect if the client doesn't have actual blindness\n                        tracker.getAffectedByDarkness().rem(entityId);\n                        if (!tracker.getAffectedByBlindness().contains(entityId)) {\n                            wrapper.set(Types.VAR_INT, 1, 15);\n                        }\n                    } else if (effectId == 15) { // Remove blindness and cancel if the client has darkness (will be removed by darkness removal)\n                        tracker.getAffectedByBlindness().rem(entityId);\n                        if (tracker.getAffectedByDarkness().contains(entityId)) {\n                            wrapper.cancel();\n                        }\n                    }\n                });\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_19.LOGIN, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.INT); // Entity ID\n                map(Types.BOOLEAN); // Hardcore\n                map(Types.BYTE); // Gamemode\n                map(Types.BYTE); // Previous Gamemode\n                map(Types.STRING_ARRAY); // Worlds\n                map(Types.NAMED_COMPOUND_TAG); // Dimension registry\n                handler(wrapper -> {\n                    final DimensionRegistryStorage dimensionRegistryStorage = wrapper.user().get(DimensionRegistryStorage.class);\n                    dimensionRegistryStorage.clear();\n\n                    // Cache dimensions and find current dimension\n                    final String dimensionKey = Key.stripMinecraftNamespace(wrapper.read(Types.STRING));\n                    final CompoundTag registry = wrapper.get(Types.NAMED_COMPOUND_TAG, 0);\n                    final ListTag<CompoundTag> dimensions = TagUtil.getRegistryEntries(registry, \"dimension_type\");\n                    boolean found = false;\n                    for (final CompoundTag dimension : dimensions) {\n                        final String name = Key.stripMinecraftNamespace(dimension.getString(\"name\"));\n                        final CompoundTag dimensionData = dimension.getCompoundTag(\"element\");\n                        dimensionRegistryStorage.addDimension(name, dimensionData.copy());\n\n                        if (!found && name.equals(dimensionKey)) {\n                            wrapper.write(Types.NAMED_COMPOUND_TAG, dimensionData);\n                            found = true;\n                        }\n                    }\n                    if (!found) {\n                        throw new IllegalArgumentException(\"Could not find dimension \" + dimensionKey + \" in dimension registry\");\n                    }\n\n                    // Add biome category and track biomes\n                    final ListTag<CompoundTag> biomes = TagUtil.getRegistryEntries(registry, \"worldgen/biome\");\n                    for (final CompoundTag biome : biomes) {\n                        final CompoundTag biomeCompound = biome.getCompoundTag(\"element\");\n                        biomeCompound.putString(\"category\", \"none\");\n                    }\n                    tracker(wrapper.user()).setBiomesSent(biomes.size());\n\n                    // Cache and remove chat types\n                    final ListTag<CompoundTag> chatTypes = TagUtil.removeRegistryEntries(registry, \"chat_type\");\n                    for (final CompoundTag chatType : chatTypes) {\n                        final NumberTag idTag = chatType.getNumberTag(\"id\");\n                        dimensionRegistryStorage.addChatType(idTag.asInt(), chatType);\n                    }\n                });\n                map(Types.STRING); // World\n                map(Types.LONG); // Seed\n                map(Types.VAR_INT); // Max players\n                map(Types.VAR_INT); // Chunk radius\n                map(Types.VAR_INT); // Simulation distance\n                map(Types.BOOLEAN); // Reduced debug info\n                map(Types.BOOLEAN); // Show death screen\n                map(Types.BOOLEAN); // Debug\n                map(Types.BOOLEAN); // Flat\n                handler(wrapper -> {\n                    final GlobalBlockPosition lastDeathPosition = wrapper.read(Types.OPTIONAL_GLOBAL_POSITION);\n                    if (lastDeathPosition != null) {\n                        wrapper.user().put(new LastDeathPosition(lastDeathPosition));\n                    } else {\n                        wrapper.user().remove(LastDeathPosition.class);\n                    }\n                });\n                handler(worldDataTrackerHandler(1));\n                handler(playerTrackerHandler());\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_19.RESPAWN, new PacketHandlers() {\n            @Override\n            public void register() {\n                handler(wrapper -> {\n                    final String dimensionKey = wrapper.read(Types.STRING);\n                    final CompoundTag dimension = wrapper.user().get(DimensionRegistryStorage.class).dimension(dimensionKey);\n                    if (dimension == null) {\n                        throw new IllegalArgumentException(\"Could not find dimension \" + dimensionKey + \" in dimension registry\");\n                    }\n\n                    wrapper.write(Types.NAMED_COMPOUND_TAG, dimension);\n                });\n                map(Types.STRING); // World\n                map(Types.LONG); // Seed\n                map(Types.UNSIGNED_BYTE); // Gamemode\n                map(Types.BYTE); // Previous gamemode\n                map(Types.BOOLEAN); // Debug\n                map(Types.BOOLEAN); // Flat\n                map(Types.BOOLEAN); // Keep player attributes\n                handler(wrapper -> {\n                    final GlobalBlockPosition lastDeathPosition = wrapper.read(Types.OPTIONAL_GLOBAL_POSITION);\n                    if (lastDeathPosition != null) {\n                        wrapper.user().put(new LastDeathPosition(lastDeathPosition));\n                    } else {\n                        wrapper.user().remove(LastDeathPosition.class);\n                    }\n                });\n                handler(worldDataTrackerHandler(0));\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_19.PLAYER_INFO, wrapper -> {\n            final int action = wrapper.passthrough(Types.VAR_INT);\n            final int entries = wrapper.passthrough(Types.VAR_INT);\n            for (int i = 0; i < entries; i++) {\n                wrapper.passthrough(Types.UUID); // UUID\n                if (action == 0) { // Add player\n                    wrapper.passthrough(Types.STRING); // Player Name\n                    wrapper.passthrough(Types.PROFILE_PROPERTY_ARRAY);\n                    wrapper.passthrough(Types.VAR_INT); // Gamemode\n                    wrapper.passthrough(Types.VAR_INT); // Ping\n                    wrapper.passthrough(Types.OPTIONAL_COMPONENT); // Display name\n\n                    // Remove public profile signature\n                    wrapper.read(Types.OPTIONAL_PROFILE_KEY);\n                } else if (action == 1 || action == 2) { // Update gamemode/update latency\n                    wrapper.passthrough(Types.VAR_INT);\n                } else if (action == 3) { // Update display name\n                    wrapper.passthrough(Types.OPTIONAL_COMPONENT);\n                }\n            }\n        });\n    }\n\n    @Override\n    protected void registerRewrites() {\n        filter().handler((event, data) -> {\n            if (data.dataType().typeId() <= Types1_18.ENTITY_DATA_TYPES.poseType.typeId()) {\n                data.setDataType(Types1_18.ENTITY_DATA_TYPES.byId(data.dataType().typeId()));\n            }\n\n            final EntityDataType type = data.dataType();\n            if (type == Types1_18.ENTITY_DATA_TYPES.particleType) {\n                final Particle particle = (Particle) data.getValue();\n                final ParticleMappings particleMappings = protocol.getMappingData().getParticleMappings();\n                if (particle.id() == particleMappings.id(\"sculk_charge\")) {\n                    //TODO\n                    event.cancel();\n                    return;\n                } else if (particle.id() == particleMappings.id(\"shriek\")) {\n                    //TODO\n                    event.cancel();\n                    return;\n                } else if (particle.id() == particleMappings.id(\"vibration\")) {\n                    // Can't do without the position\n                    event.cancel();\n                    return;\n                }\n\n                protocol.getParticleRewriter().rewriteParticle(event.user(), particle);\n            } else if (type == Types1_18.ENTITY_DATA_TYPES.poseType) {\n                final int pose = data.value();\n                if (pose >= 8) {\n                    // Croaking, using_tongue, roaring, sniffing, emerging, digging -> standing -> standing\n                    data.setValue(0);\n                }\n            }\n        });\n\n        registerEntityDataTypeHandler(Types1_18.ENTITY_DATA_TYPES.itemType, null, Types1_18.ENTITY_DATA_TYPES.optionalBlockStateType, null,\n            Types1_18.ENTITY_DATA_TYPES.componentType, Types1_18.ENTITY_DATA_TYPES.optionalComponentType);\n        registerBlockStateHandler(EntityTypes1_19.ABSTRACT_MINECART, 11);\n\n        filter().type(EntityTypes1_19.PAINTING).index(8).handler((event, data) -> {\n            event.cancel();\n\n            final StoredEntityData entityData = tracker(event.user()).entityDataIfPresent(event.entityId());\n            final StoredPainting storedPainting = entityData != null ? entityData.get(StoredPainting.class) : null;\n            if (storedPainting != null) {\n                final int type = data.value();\n                storedPainting.setType(type);\n\n                final PacketWrapper packet = PacketWrapper.create(ClientboundPackets1_18.ADD_PAINTING, event.user());\n                packet.write(Types.VAR_INT, storedPainting.entityId());\n                packet.write(Types.UUID, storedPainting.uuid());\n                packet.write(Types.VAR_INT, type);\n                packet.write(Types.BLOCK_POSITION1_14, storedPainting.position());\n                packet.write(Types.BYTE, storedPainting.direction());\n                try {\n                    // TODO Race condition\n                    packet.send(Protocol1_19To1_18_2.class);\n                } catch (Exception e) {\n                    throw new RuntimeException(e);\n                }\n            }\n        });\n\n        filter().type(EntityTypes1_19.CAT).index(19).handler((event, data) -> data.setDataType(Types1_18.ENTITY_DATA_TYPES.varIntType));\n\n        filter().type(EntityTypes1_19.FROG).cancel(16); // Age\n        filter().type(EntityTypes1_19.FROG).cancel(17); // Variant\n        filter().type(EntityTypes1_19.FROG).cancel(18); // Tongue target\n\n        filter().type(EntityTypes1_19.WARDEN).cancel(16); // Anger\n\n        filter().type(EntityTypes1_19.GOAT).cancel(18);\n        filter().type(EntityTypes1_19.GOAT).cancel(19);\n    }\n\n    @Override\n    public void onMappingDataLoaded() {\n        super.onMappingDataLoaded();\n        mapEntityTypeWithData(EntityTypes1_19.FROG, EntityTypes1_19.RABBIT).jsonName();\n        mapEntityTypeWithData(EntityTypes1_19.TADPOLE, EntityTypes1_19.PUFFERFISH).jsonName();\n        mapEntityTypeWithData(EntityTypes1_19.CHEST_BOAT, EntityTypes1_19.BOAT);\n        mapEntityTypeWithData(EntityTypes1_19.WARDEN, EntityTypes1_19.IRON_GOLEM).jsonName();\n        mapEntityTypeWithData(EntityTypes1_19.ALLAY, EntityTypes1_19.VEX).jsonName();\n    }\n\n    @Override\n    public EntityType typeFromId(final int typeId) {\n        return EntityTypes1_19.getTypeFromId(typeId);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_19to1_18_2/storage/DimensionRegistryStorage.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_19to1_18_2.storage;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.viabackwards.protocol.v1_19to1_18_2.Protocol1_19To1_18_2;\nimport com.viaversion.viaversion.api.connection.StorableObject;\nimport com.viaversion.viaversion.libs.fastutil.ints.Int2ObjectMap;\nimport com.viaversion.viaversion.libs.fastutil.ints.Int2ObjectOpenHashMap;\nimport com.viaversion.viaversion.util.Key;\nimport java.util.HashMap;\nimport java.util.Map;\nimport org.checkerframework.checker.nullness.qual.Nullable;\n\npublic final class DimensionRegistryStorage implements StorableObject {\n\n    private final Map<String, CompoundTag> dimensions = new HashMap<>();\n    private final Int2ObjectMap<CompoundTag> chatTypes = new Int2ObjectOpenHashMap<>();\n\n    public @Nullable CompoundTag dimension(final String dimensionKey) {\n        final CompoundTag compoundTag = dimensions.get(Key.stripMinecraftNamespace(dimensionKey));\n        return compoundTag != null ? compoundTag.copy() : null;\n    }\n\n    public void addDimension(final String dimensionKey, final CompoundTag dimension) {\n        dimensions.put(dimensionKey, dimension);\n    }\n\n    public @Nullable CompoundTag chatType(final int id) {\n        return chatTypes.isEmpty() ? Protocol1_19To1_18_2.MAPPINGS.chatType(id) : chatTypes.get(id);\n    }\n\n    public void addChatType(final int id, final CompoundTag chatType) {\n        chatTypes.put(id, chatType);\n    }\n\n    public void clear() {\n        dimensions.clear();\n        chatTypes.clear();\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_19to1_18_2/storage/EntityTracker1_19.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_19to1_18_2.storage;\n\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_19;\nimport com.viaversion.viaversion.data.entity.EntityTrackerBase;\nimport com.viaversion.viaversion.libs.fastutil.ints.IntArrayList;\nimport com.viaversion.viaversion.libs.fastutil.ints.IntList;\n\npublic final class EntityTracker1_19 extends EntityTrackerBase {\n\n    private final IntList affectedByBlindness = new IntArrayList();\n    private final IntList affectedByDarkness = new IntArrayList();\n\n    public EntityTracker1_19(final UserConnection connection) {\n        super(connection, EntityTypes1_19.PLAYER);\n    }\n\n    @Override\n    public void removeEntity(final int id) {\n        super.removeEntity(id);\n        this.affectedByBlindness.rem(id);\n        this.affectedByDarkness.rem(id);\n    }\n\n    public IntList getAffectedByBlindness() {\n        return affectedByBlindness;\n    }\n\n    public IntList getAffectedByDarkness() {\n        return affectedByDarkness;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_19to1_18_2/storage/LastDeathPosition.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_19to1_18_2.storage;\n\nimport com.viaversion.viaversion.api.connection.StorableObject;\nimport com.viaversion.viaversion.api.minecraft.GlobalBlockPosition;\n\npublic record LastDeathPosition(GlobalBlockPosition position) implements StorableObject {\n\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_19to1_18_2/storage/NonceStorage.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_19to1_18_2.storage;\n\nimport com.viaversion.viaversion.api.connection.StorableObject;\nimport org.checkerframework.checker.nullness.qual.Nullable;\n\npublic record NonceStorage(byte @Nullable [] nonce) implements StorableObject {\n\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_19to1_18_2/storage/StoredPainting.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_19to1_18_2.storage;\n\nimport com.viaversion.viaversion.api.connection.StorableObject;\nimport com.viaversion.viaversion.api.minecraft.BlockPosition;\nimport java.util.UUID;\n\npublic final class StoredPainting implements StorableObject {\n    private final int entityId;\n    private final UUID uuid;\n    private final BlockPosition position;\n    private final byte direction;\n    private int type;\n\n    public StoredPainting(int entityId, UUID uuid, BlockPosition position, byte direction) {\n        this.entityId = entityId;\n        this.uuid = uuid;\n        this.position = position;\n        this.direction = direction;\n    }\n\n    public StoredPainting(final int entityId, final UUID uuid, final BlockPosition position, final int direction) {\n        this(entityId, uuid, position, to2dDirection(direction));\n    }\n\n    private static byte to2dDirection(int direction) {\n        return switch (direction) {\n            case 0, 1 -> -1; // No worky\n            case 2 -> 2;\n            case 3 -> 0;\n            case 4 -> 1;\n            case 5 -> 3;\n            default -> throw new IllegalArgumentException(\"Invalid direction: \" + direction);\n        };\n    }\n\n    public int entityId() {\n        return entityId;\n    }\n\n    public UUID uuid() {\n        return uuid;\n    }\n\n    public BlockPosition position() {\n        return position;\n    }\n\n    public byte direction() {\n        return direction;\n    }\n\n    public int type() {\n        return type;\n    }\n\n    public void setType(final int type) {\n        this.type = type;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_20_2to1_20/Protocol1_20_2To1_20.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_20_2to1_20;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viabackwards.api.data.BackwardsMappingData;\nimport com.viaversion.viabackwards.protocol.v1_20_2to1_20.provider.AdvancementCriteriaProvider;\nimport com.viaversion.viabackwards.protocol.v1_20_2to1_20.rewriter.BlockItemPacketRewriter1_20_2;\nimport com.viaversion.viabackwards.protocol.v1_20_2to1_20.rewriter.BlockRewriter1_20_2;\nimport com.viaversion.viabackwards.protocol.v1_20_2to1_20.rewriter.EntityPacketRewriter1_20_2;\nimport com.viaversion.viabackwards.protocol.v1_20_2to1_20.storage.ConfigurationPacketStorage;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_19_4;\nimport com.viaversion.viaversion.api.platform.providers.ViaProviders;\nimport com.viaversion.viaversion.api.protocol.packet.Direction;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.protocol.packet.State;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.data.entity.EntityTrackerBase;\nimport com.viaversion.viaversion.exception.CancelException;\nimport com.viaversion.viaversion.exception.InformativeException;\nimport com.viaversion.viaversion.protocols.base.ClientboundLoginPackets;\nimport com.viaversion.viaversion.protocols.base.ServerboundLoginPackets;\nimport com.viaversion.viaversion.protocols.v1_19_3to1_19_4.packet.ClientboundPackets1_19_4;\nimport com.viaversion.viaversion.protocols.v1_19_3to1_19_4.packet.ServerboundPackets1_19_4;\nimport com.viaversion.viaversion.protocols.v1_20to1_20_2.Protocol1_20To1_20_2;\nimport com.viaversion.viaversion.protocols.v1_20to1_20_2.packet.ClientboundConfigurationPackets1_20_2;\nimport com.viaversion.viaversion.protocols.v1_20to1_20_2.packet.ClientboundPackets1_20_2;\nimport com.viaversion.viaversion.protocols.v1_20to1_20_2.packet.ServerboundConfigurationPackets1_20_2;\nimport com.viaversion.viaversion.protocols.v1_20to1_20_2.packet.ServerboundPackets1_20_2;\nimport com.viaversion.viaversion.rewriter.BlockRewriter;\nimport com.viaversion.viaversion.rewriter.ParticleRewriter;\nimport com.viaversion.viaversion.rewriter.TagRewriter;\nimport java.util.UUID;\n\npublic final class Protocol1_20_2To1_20 extends BackwardsProtocol<ClientboundPackets1_20_2, ClientboundPackets1_19_4, ServerboundPackets1_20_2, ServerboundPackets1_19_4> {\n\n    public static final BackwardsMappingData MAPPINGS = new BackwardsMappingData(\"1.20.2\", \"1.20\", Protocol1_20To1_20_2.class);\n    private final EntityPacketRewriter1_20_2 entityPacketRewriter = new EntityPacketRewriter1_20_2(this);\n    private final BlockItemPacketRewriter1_20_2 itemPacketRewriter = new BlockItemPacketRewriter1_20_2(this);\n    private final ParticleRewriter<ClientboundPackets1_20_2> particleRewriter = new ParticleRewriter<>(this);\n    private final TagRewriter<ClientboundPackets1_20_2> tagRewriter = new TagRewriter<>(this);\n    private final BlockRewriter<ClientboundPackets1_20_2> blockRewriter = new BlockRewriter1_20_2(this);\n\n    public Protocol1_20_2To1_20() {\n        super(ClientboundPackets1_20_2.class, ClientboundPackets1_19_4.class, ServerboundPackets1_20_2.class, ServerboundPackets1_19_4.class);\n    }\n\n    @Override\n    protected void registerPackets() {\n        super.registerPackets();\n\n        registerClientbound(ClientboundPackets1_20_2.SET_DISPLAY_OBJECTIVE, wrapper -> {\n            final int slot = wrapper.read(Types.VAR_INT);\n            wrapper.write(Types.BYTE, (byte) slot);\n        });\n\n        registerClientbound(State.LOGIN, ClientboundLoginPackets.LOGIN_FINISHED, wrapper -> {\n            // We can't set the internal state to configuration here as protocols down the line will expect the state to be play\n            // Add this *before* sending the ack since the server might immediately answer\n            wrapper.user().put(new ConfigurationPacketStorage());\n\n            // Overwrite what is set by the base protocol\n            wrapper.user().getProtocolInfo().setClientState(State.LOGIN);\n\n            // States set to configuration in the base protocol\n            wrapper.create(ServerboundLoginPackets.LOGIN_ACKNOWLEDGED).scheduleSendToServer(Protocol1_20_2To1_20.class);\n        });\n\n        registerClientbound(State.CONFIGURATION, ClientboundConfigurationPackets1_20_2.FINISH_CONFIGURATION, wrapper -> {\n            wrapper.cancel();\n            wrapper.user().getProtocolInfo().setServerState(State.PLAY);\n            wrapper.user().get(ConfigurationPacketStorage.class).setFinished(true);\n\n            wrapper.create(ServerboundConfigurationPackets1_20_2.FINISH_CONFIGURATION).sendToServer(Protocol1_20_2To1_20.class);\n            wrapper.user().getProtocolInfo().setClientState(State.PLAY);\n        });\n\n        registerServerbound(State.LOGIN, ServerboundLoginPackets.HELLO, wrapper -> {\n            wrapper.passthrough(Types.STRING); // Name\n\n            // TODO Bad\n            final UUID uuid = wrapper.read(Types.OPTIONAL_UUID);\n            wrapper.write(Types.UUID, uuid != null ? uuid : new UUID(0, 0));\n        });\n\n        registerClientbound(ClientboundPackets1_20_2.START_CONFIGURATION, null, wrapper -> {\n            wrapper.cancel();\n            wrapper.user().getProtocolInfo().setServerState(State.CONFIGURATION);\n\n            // TODO: Check whether all the necessary data for the join game packet is always expected by the client or if we need to cache it from the initial login\n            final PacketWrapper configAcknowledgedPacket = wrapper.create(ServerboundPackets1_20_2.CONFIGURATION_ACKNOWLEDGED);\n            configAcknowledgedPacket.sendToServer(Protocol1_20_2To1_20.class);\n            wrapper.user().getProtocolInfo().setClientState(State.CONFIGURATION);\n            wrapper.user().put(new ConfigurationPacketStorage());\n        });\n        cancelClientbound(ClientboundPackets1_20_2.PONG_RESPONSE);\n\n        // Some can be directly remapped to play packets, others need to be queued\n        // Set the packet type properly so the state on it is changed\n        registerClientbound(State.CONFIGURATION, ClientboundConfigurationPackets1_20_2.DISCONNECT.getId(), -1, wrapper -> {\n            wrapper.setPacketType(ClientboundPackets1_19_4.DISCONNECT);\n        });\n        registerClientbound(State.CONFIGURATION, ClientboundConfigurationPackets1_20_2.KEEP_ALIVE.getId(), -1, wrapper -> {\n            wrapper.setPacketType(ClientboundPackets1_19_4.KEEP_ALIVE);\n        });\n        registerClientbound(State.CONFIGURATION, ClientboundConfigurationPackets1_20_2.PING.getId(), -1, wrapper -> {\n            wrapper.setPacketType(ClientboundPackets1_19_4.PING);\n        });\n        registerClientbound(State.CONFIGURATION, ClientboundConfigurationPackets1_20_2.RESOURCE_PACK.getId(), -1, wrapper -> {\n            // Send after join. We have to pretend the client accepted, else the server won't continue...\n            wrapper.user().get(ConfigurationPacketStorage.class).setResourcePack(wrapper);\n            wrapper.cancel();\n\n            final PacketWrapper acceptedResponse = wrapper.create(ServerboundConfigurationPackets1_20_2.RESOURCE_PACK);\n            acceptedResponse.write(Types.VAR_INT, 3);\n            acceptedResponse.sendToServer(Protocol1_20_2To1_20.class);\n\n            final PacketWrapper downloadedResponse = wrapper.create(ServerboundConfigurationPackets1_20_2.RESOURCE_PACK);\n            downloadedResponse.write(Types.VAR_INT, 0);\n            downloadedResponse.sendToServer(Protocol1_20_2To1_20.class);\n        });\n        registerClientbound(State.CONFIGURATION, ClientboundConfigurationPackets1_20_2.REGISTRY_DATA.getId(), -1, wrapper -> {\n            wrapper.cancel();\n\n            final CompoundTag registry = wrapper.read(Types.COMPOUND_TAG);\n            entityPacketRewriter.trackBiomeSize(wrapper.user(), registry);\n            entityPacketRewriter.cacheDimensionData(wrapper.user(), registry);\n            wrapper.user().get(ConfigurationPacketStorage.class).setRegistry(registry);\n        });\n        registerClientbound(State.CONFIGURATION, ClientboundConfigurationPackets1_20_2.UPDATE_ENABLED_FEATURES.getId(), -1, wrapper -> {\n            final String[] enabledFeatures = wrapper.read(Types.STRING_ARRAY);\n            wrapper.user().get(ConfigurationPacketStorage.class).setEnabledFeatures(enabledFeatures);\n            wrapper.cancel();\n        });\n        registerClientbound(State.CONFIGURATION, ClientboundConfigurationPackets1_20_2.UPDATE_TAGS.getId(), -1, wrapper -> {\n            tagRewriter.handleGeneric(wrapper);\n            wrapper.user().get(ConfigurationPacketStorage.class).addRawPacket(wrapper, ClientboundPackets1_19_4.UPDATE_TAGS);\n            wrapper.cancel();\n        });\n        registerClientbound(State.CONFIGURATION, ClientboundConfigurationPackets1_20_2.CUSTOM_PAYLOAD.getId(), -1, wrapper -> {\n            wrapper.user().get(ConfigurationPacketStorage.class).addRawPacket(wrapper, ClientboundPackets1_19_4.CUSTOM_PAYLOAD);\n            wrapper.cancel();\n        });\n    }\n\n    @Override\n    public void register(final ViaProviders providers) {\n        providers.register(AdvancementCriteriaProvider.class, new AdvancementCriteriaProvider());\n    }\n\n    @Override\n    public void transform(final Direction direction, final State state, final PacketWrapper wrapper) throws InformativeException, CancelException {\n        final ConfigurationPacketStorage configurationPacketStorage = wrapper.user().get(ConfigurationPacketStorage.class);\n        if (configurationPacketStorage == null || configurationPacketStorage.isFinished()) {\n            super.transform(direction, state, wrapper);\n            return;\n        }\n        if (direction == Direction.CLIENTBOUND) {\n            super.transform(direction, State.CONFIGURATION, wrapper);\n            return;\n        }\n\n        // Map some of the packets to their configuration counterparts\n        final int id = wrapper.getId();\n        if (id == ServerboundPackets1_19_4.CLIENT_INFORMATION.getId()) {\n            wrapper.setPacketType(ServerboundConfigurationPackets1_20_2.CLIENT_INFORMATION);\n        } else if (id == ServerboundPackets1_19_4.CUSTOM_PAYLOAD.getId()) {\n            wrapper.setPacketType(ServerboundConfigurationPackets1_20_2.CUSTOM_PAYLOAD);\n        } else if (id == ServerboundPackets1_19_4.KEEP_ALIVE.getId()) {\n            wrapper.setPacketType(ServerboundConfigurationPackets1_20_2.KEEP_ALIVE);\n        } else if (id == ServerboundPackets1_19_4.PONG.getId()) {\n            wrapper.setPacketType(ServerboundConfigurationPackets1_20_2.PONG);\n        } else if (id == ServerboundPackets1_19_4.RESOURCE_PACK.getId()) {\n            wrapper.setPacketType(ServerboundConfigurationPackets1_20_2.RESOURCE_PACK);\n        } else {\n            // TODO Queue\n            throw CancelException.generate();\n        }\n    }\n\n    @Override\n    protected void registerConfigurationChangeHandlers() {\n        // Don't register them in the transitioning protocol\n    }\n\n    @Override\n    public void init(final UserConnection connection) {\n        addEntityTracker(connection, new EntityTrackerBase(connection, EntityTypes1_19_4.PLAYER));\n    }\n\n    @Override\n    public BackwardsMappingData getMappingData() {\n        return MAPPINGS;\n    }\n\n    @Override\n    public EntityPacketRewriter1_20_2 getEntityRewriter() {\n        return entityPacketRewriter;\n    }\n\n    @Override\n    public BlockItemPacketRewriter1_20_2 getItemRewriter() {\n        return itemPacketRewriter;\n    }\n\n    @Override\n    public BlockRewriter<ClientboundPackets1_20_2> getBlockRewriter() {\n        return blockRewriter;\n    }\n\n    @Override\n    public ParticleRewriter<ClientboundPackets1_20_2> getParticleRewriter() {\n        return particleRewriter;\n    }\n\n    @Override\n    public TagRewriter<ClientboundPackets1_20_2> getTagRewriter() {\n        return tagRewriter;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_20_2to1_20/provider/AdvancementCriteriaProvider.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_20_2to1_20.provider;\n\nimport com.viaversion.viaversion.api.platform.providers.Provider;\n\npublic class AdvancementCriteriaProvider implements Provider {\n\n    public String[] getCriteria(final String advancementKey) {\n        return new String[0];\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_20_2to1_20/rewriter/BlockItemPacketRewriter1_20_2.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_20_2to1_20.rewriter;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.IntArrayTag;\nimport com.viaversion.viabackwards.api.rewriters.BackwardsItemRewriter;\nimport com.viaversion.viabackwards.protocol.v1_20_2to1_20.Protocol1_20_2To1_20;\nimport com.viaversion.viabackwards.protocol.v1_20_2to1_20.provider.AdvancementCriteriaProvider;\nimport com.viaversion.viaversion.api.Via;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.data.entity.EntityTracker;\nimport com.viaversion.viaversion.api.minecraft.BlockPosition;\nimport com.viaversion.viaversion.api.minecraft.ChunkPosition;\nimport com.viaversion.viaversion.api.minecraft.blockentity.BlockEntity;\nimport com.viaversion.viaversion.api.minecraft.blockentity.BlockEntityImpl;\nimport com.viaversion.viaversion.api.minecraft.chunks.Chunk;\nimport com.viaversion.viaversion.api.minecraft.chunks.ChunkSection;\nimport com.viaversion.viaversion.api.minecraft.chunks.DataPalette;\nimport com.viaversion.viaversion.api.minecraft.chunks.PaletteType;\nimport com.viaversion.viaversion.api.minecraft.item.Item;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.type.Type;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.chunk.ChunkType1_18;\nimport com.viaversion.viaversion.api.type.types.chunk.ChunkType1_20_2;\nimport com.viaversion.viaversion.protocols.v1_19_3to1_19_4.packet.ServerboundPackets1_19_4;\nimport com.viaversion.viaversion.protocols.v1_20to1_20_2.packet.ClientboundPackets1_20_2;\nimport com.viaversion.viaversion.protocols.v1_20to1_20_2.packet.ServerboundPackets1_20_2;\nimport com.viaversion.viaversion.protocols.v1_20to1_20_2.rewriter.RecipeRewriter1_20_2;\nimport com.viaversion.viaversion.util.MathUtil;\nimport org.checkerframework.checker.nullness.qual.Nullable;\n\npublic final class BlockItemPacketRewriter1_20_2 extends BackwardsItemRewriter<ClientboundPackets1_20_2, ServerboundPackets1_19_4, Protocol1_20_2To1_20> {\n\n    public BlockItemPacketRewriter1_20_2(final Protocol1_20_2To1_20 protocol) {\n        super(protocol, Types.ITEM1_20_2, Types.ITEM1_20_2_ARRAY, Types.ITEM1_13_2, Types.ITEM1_13_2_ARRAY);\n    }\n\n    @Override\n    public void registerPackets() {\n        protocol.cancelClientbound(ClientboundPackets1_20_2.CHUNK_BATCH_START);\n        protocol.registerClientbound(ClientboundPackets1_20_2.CHUNK_BATCH_FINISHED, null, wrapper -> {\n            wrapper.cancel();\n\n            final PacketWrapper receivedPacket = wrapper.create(ServerboundPackets1_20_2.CHUNK_BATCH_RECEIVED);\n            receivedPacket.write(Types.FLOAT, 500F); // Requested next batch size... arbitrary value here\n            receivedPacket.sendToServer(Protocol1_20_2To1_20.class);\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_20_2.FORGET_LEVEL_CHUNK, wrapper -> {\n            final ChunkPosition chunkPosition = wrapper.read(Types.CHUNK_POSITION);\n            wrapper.write(Types.INT, chunkPosition.chunkX());\n            wrapper.write(Types.INT, chunkPosition.chunkZ());\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_20_2.MAP_ITEM_DATA, wrapper -> {\n            wrapper.passthrough(Types.VAR_INT); // Map id\n            wrapper.passthrough(Types.BYTE); // Scale\n            wrapper.passthrough(Types.BOOLEAN); // Locked\n            if (wrapper.passthrough(Types.BOOLEAN)) {\n                final int icons = wrapper.passthrough(Types.VAR_INT);\n                for (int i = 0; i < icons; i++) {\n                    // Map new marker types to red marker\n                    final int markerType = wrapper.read(Types.VAR_INT);\n                    wrapper.write(Types.VAR_INT, markerType < 27 ? markerType : 2);\n\n                    wrapper.passthrough(Types.BYTE); // X\n                    wrapper.passthrough(Types.BYTE); // Y\n                    wrapper.passthrough(Types.BYTE); // Rotation\n                    wrapper.passthrough(Types.OPTIONAL_COMPONENT); // Display name\n                }\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_20_2.TAG_QUERY, wrapper -> {\n            wrapper.passthrough(Types.VAR_INT); // Transaction id\n            wrapper.write(Types.NAMED_COMPOUND_TAG, wrapper.read(Types.COMPOUND_TAG));\n        });\n\n        protocol.replaceClientbound(ClientboundPackets1_20_2.BLOCK_ENTITY_DATA, wrapper -> {\n            final BlockPosition position = wrapper.passthrough(Types.BLOCK_POSITION1_14);\n            final int typeId = wrapper.passthrough(Types.VAR_INT);\n\n            final CompoundTag tag = wrapper.read(Types.TRUSTED_COMPOUND_TAG);\n            final BlockEntity blockEntity = new BlockEntityImpl(BlockEntity.pack(position.x(), position.z()), (short) position.y(), typeId, tag);\n            protocol.getBlockRewriter().handleBlockEntity(wrapper.user(), blockEntity);\n            wrapper.write(Types.NAMED_COMPOUND_TAG, blockEntity.tag());\n        });\n\n        protocol.registerServerbound(ServerboundPackets1_19_4.SET_BEACON, wrapper -> {\n            // Effects start at 1 before 1.20.2\n            if (wrapper.passthrough(Types.BOOLEAN)) { // Primary effect\n                wrapper.write(Types.VAR_INT, wrapper.read(Types.VAR_INT) - 1);\n            }\n            if (wrapper.passthrough(Types.BOOLEAN)) { // Secondary effect\n                wrapper.write(Types.VAR_INT, wrapper.read(Types.VAR_INT) - 1);\n            }\n        });\n\n        protocol.replaceClientbound(ClientboundPackets1_20_2.UPDATE_ADVANCEMENTS, wrapper -> {\n            wrapper.passthrough(Types.BOOLEAN); // Reset/clear\n            final int size = wrapper.passthrough(Types.VAR_INT);\n            for (int i = 0; i < size; i++) {\n                final String advancement = wrapper.passthrough(Types.STRING);\n                wrapper.passthrough(Types.OPTIONAL_STRING); // Parent\n\n                // Display data\n                if (wrapper.passthrough(Types.BOOLEAN)) {\n                    wrapper.passthrough(Types.COMPONENT); // Title\n                    wrapper.passthrough(Types.COMPONENT); // Description\n                    passthroughClientboundItem(wrapper); // Icon\n                    wrapper.passthrough(Types.VAR_INT); // Frame type\n                    final int flags = wrapper.passthrough(Types.INT); // Flags\n                    if ((flags & 1) != 0) {\n                        wrapper.passthrough(Types.STRING); // Background texture\n                    }\n                    wrapper.passthrough(Types.FLOAT); // X\n                    wrapper.passthrough(Types.FLOAT); // Y\n                }\n\n                final AdvancementCriteriaProvider criteriaProvider = Via.getManager().getProviders().get(AdvancementCriteriaProvider.class);\n                wrapper.write(Types.STRING_ARRAY, criteriaProvider.getCriteria(advancement));\n\n                final int requirements = wrapper.passthrough(Types.VAR_INT);\n                for (int array = 0; array < requirements; array++) {\n                    wrapper.passthrough(Types.STRING_ARRAY);\n                }\n\n                wrapper.passthrough(Types.BOOLEAN); // Send telemetry\n            }\n        });\n        protocol.replaceClientbound(ClientboundPackets1_20_2.SET_EQUIPMENT, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT); // 0 - Entity ID\n                handler(wrapper -> {\n                    byte slot;\n                    do {\n                        slot = wrapper.passthrough(Types.BYTE);\n                        wrapper.write(Types.ITEM1_13_2, handleItemToClient(wrapper.user(), wrapper.read(Types.ITEM1_20_2)));\n                    } while ((slot & 0xFFFFFF80) != 0);\n                });\n            }\n        });\n\n        new RecipeRewriter1_20_2<>(protocol) {\n            @Override\n            protected Type<Item> mappedItemType() {\n                return BlockItemPacketRewriter1_20_2.this.mappedItemType();\n            }\n\n            @Override\n            protected Type<Item[]> mappedItemArrayType() {\n                return BlockItemPacketRewriter1_20_2.this.mappedItemArrayType();\n            }\n\n        }.register(ClientboundPackets1_20_2.UPDATE_RECIPES);\n    }\n\n    @Override\n    public @Nullable Item handleItemToClient(UserConnection connection, @Nullable final Item item) {\n        if (item == null) {\n            return null;\n        }\n        if (item.tag() != null) {\n            com.viaversion.viaversion.protocols.v1_20to1_20_2.rewriter.BlockItemPacketRewriter1_20_2.to1_20_1Effects(item);\n\n            final CompoundTag skullOwnerTag = item.tag().getCompoundTag(\"SkullOwner\");\n            if (skullOwnerTag != null && !skullOwnerTag.contains(\"Id\") && skullOwnerTag.contains(\"Properties\")) {\n                skullOwnerTag.put(\"Id\", new IntArrayTag(new int[]{0, 0, 0, 0}));\n            }\n        }\n\n        return super.handleItemToClient(connection, item);\n    }\n\n    @Override\n    public @Nullable Item handleItemToServer(UserConnection connection, @Nullable final Item item) {\n        if (item == null) {\n            return null;\n        }\n        if (item.tag() != null) {\n            com.viaversion.viaversion.protocols.v1_20to1_20_2.rewriter.BlockItemPacketRewriter1_20_2.to1_20_2Effects(item);\n        }\n\n        return super.handleItemToServer(connection, item);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_20_2to1_20/rewriter/BlockRewriter1_20_2.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_20_2to1_20.rewriter;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.IntArrayTag;\nimport com.viaversion.nbt.tag.StringTag;\nimport com.viaversion.nbt.tag.Tag;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.blockentity.BlockEntity;\nimport com.viaversion.viaversion.api.protocol.Protocol;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.chunk.ChunkType1_18;\nimport com.viaversion.viaversion.api.type.types.chunk.ChunkType1_20_2;\nimport com.viaversion.viaversion.protocols.v1_20to1_20_2.data.PotionEffects1_20_2;\nimport com.viaversion.viaversion.protocols.v1_20to1_20_2.packet.ClientboundPackets1_20_2;\nimport com.viaversion.viaversion.rewriter.BlockRewriter;\nimport com.viaversion.viaversion.util.Key;\n\npublic final class BlockRewriter1_20_2 extends BlockRewriter<ClientboundPackets1_20_2> {\n\n    public BlockRewriter1_20_2(final Protocol<ClientboundPackets1_20_2, ?, ?, ?> protocol) {\n        super(protocol, Types.BLOCK_POSITION1_14, Types.NAMED_COMPOUND_TAG, ChunkType1_20_2::new, ChunkType1_18::new);\n    }\n\n    @Override\n    public void handleBlockEntity(final UserConnection connection, final BlockEntity blockEntity) {\n        final CompoundTag tag = blockEntity.tag();\n        final Tag primaryEffect = tag.remove(\"primary_effect\");\n        if (primaryEffect instanceof StringTag) {\n            final String effectKey = Key.stripMinecraftNamespace(((StringTag) primaryEffect).getValue());\n            tag.putInt(\"Primary\", PotionEffects1_20_2.keyToId(effectKey) + 1); // Empty effect at 0\n        }\n\n        final Tag secondaryEffect = tag.remove(\"secondary_effect\");\n        if (secondaryEffect instanceof StringTag) {\n            final String effectKey = Key.stripMinecraftNamespace(((StringTag) secondaryEffect).getValue());\n            tag.putInt(\"Secondary\", PotionEffects1_20_2.keyToId(effectKey) + 1); // Empty effect at 0\n        }\n\n        final CompoundTag skullOwnerTag = tag.getCompoundTag(\"SkullOwner\");\n        if (skullOwnerTag != null && !skullOwnerTag.contains(\"Id\") && skullOwnerTag.contains(\"Properties\")) {\n            skullOwnerTag.put(\"Id\", new IntArrayTag(new int[]{0, 0, 0, 0}));\n        }\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_20_2to1_20/rewriter/EntityPacketRewriter1_20_2.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_20_2to1_20.rewriter;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.viabackwards.api.rewriters.EntityRewriter;\nimport com.viaversion.viabackwards.protocol.v1_20_2to1_20.Protocol1_20_2To1_20;\nimport com.viaversion.viabackwards.protocol.v1_20_2to1_20.storage.ConfigurationPacketStorage;\nimport com.viaversion.viaversion.api.minecraft.GlobalBlockPosition;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityType;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_19_4;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.version.Types1_20;\nimport com.viaversion.viaversion.api.type.types.version.Types1_20_2;\nimport com.viaversion.viaversion.protocols.v1_19_3to1_19_4.packet.ClientboundPackets1_19_4;\nimport com.viaversion.viaversion.protocols.v1_20to1_20_2.packet.ClientboundPackets1_20_2;\n\npublic final class EntityPacketRewriter1_20_2 extends EntityRewriter<ClientboundPackets1_20_2, Protocol1_20_2To1_20> {\n\n    public EntityPacketRewriter1_20_2(final Protocol1_20_2To1_20 protocol) {\n        super(protocol, Types1_20.ENTITY_DATA_TYPES.optionalComponentType, Types1_20.ENTITY_DATA_TYPES.booleanType);\n    }\n\n    @Override\n    public void registerPackets() {\n        registerSetEntityData(ClientboundPackets1_20_2.SET_ENTITY_DATA, Types1_20_2.ENTITY_DATA_LIST, Types1_20.ENTITY_DATA_LIST);\n\n        protocol.replaceClientbound(ClientboundPackets1_20_2.ADD_ENTITY, new PacketHandlers() {\n            @Override\n            protected void register() {\n                handler(wrapper -> {\n                    final int entityId = wrapper.passthrough(Types.VAR_INT);\n\n                    wrapper.passthrough(Types.UUID); // UUID\n\n                    final int entityType = wrapper.read(Types.VAR_INT);\n                    tracker(wrapper.user()).addEntity(entityId, typeFromId(entityType));\n\n                    if (entityType != EntityTypes1_19_4.PLAYER.getId()) {\n                        wrapper.write(Types.VAR_INT, entityType);\n\n                        if (entityType == EntityTypes1_19_4.FALLING_BLOCK.getId()) {\n                            wrapper.passthrough(Types.DOUBLE); // X\n                            wrapper.passthrough(Types.DOUBLE); // Y\n                            wrapper.passthrough(Types.DOUBLE); // Z\n                            wrapper.passthrough(Types.BYTE); // Pitch\n                            wrapper.passthrough(Types.BYTE); // Yaw\n                            wrapper.passthrough(Types.BYTE); // Head yaw\n                            final int blockState = wrapper.read(Types.VAR_INT); // Data\n                            wrapper.write(Types.VAR_INT, protocol.getMappingData().getNewBlockStateId(blockState));\n                        }\n                        return;\n                    }\n\n                    // Map to spawn player packet\n                    wrapper.setPacketType(ClientboundPackets1_19_4.ADD_PLAYER);\n\n                    wrapper.passthrough(Types.DOUBLE); // X\n                    wrapper.passthrough(Types.DOUBLE); // Y\n                    wrapper.passthrough(Types.DOUBLE); // Z\n\n                    final byte pitch = wrapper.read(Types.BYTE);\n                    wrapper.passthrough(Types.BYTE); // Yaw\n                    wrapper.write(Types.BYTE, pitch);\n                    wrapper.read(Types.BYTE); // Head yaw\n                    wrapper.read(Types.VAR_INT); // Data\n\n                    final short velocityX = wrapper.read(Types.SHORT);\n                    final short velocityY = wrapper.read(Types.SHORT);\n                    final short velocityZ = wrapper.read(Types.SHORT);\n                    if (velocityX == 0 && velocityY == 0 && velocityZ == 0) {\n                        return;\n                    }\n\n                    // Follow up with velocity packet\n                    wrapper.send(Protocol1_20_2To1_20.class);\n                    wrapper.cancel();\n\n                    final PacketWrapper velocityPacket = wrapper.create(ClientboundPackets1_19_4.SET_ENTITY_MOTION);\n                    velocityPacket.write(Types.VAR_INT, entityId);\n                    velocityPacket.write(Types.SHORT, velocityX);\n                    velocityPacket.write(Types.SHORT, velocityY);\n                    velocityPacket.write(Types.SHORT, velocityZ);\n                    velocityPacket.send(Protocol1_20_2To1_20.class);\n                });\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_20_2.LOGIN, new PacketHandlers() {\n            @Override\n            public void register() {\n                handler(wrapper -> {\n                    final ConfigurationPacketStorage configurationPacketStorage = wrapper.user().get(ConfigurationPacketStorage.class);\n                    wrapper.passthrough(Types.INT); // Entity id\n                    wrapper.passthrough(Types.BOOLEAN); // Hardcore\n\n                    final String[] worlds = wrapper.read(Types.STRING_ARRAY);\n                    final int maxPlayers = wrapper.read(Types.VAR_INT);\n                    final int viewDistance = wrapper.read(Types.VAR_INT);\n                    final int simulationDistance = wrapper.read(Types.VAR_INT);\n                    final boolean reducedDebugInfo = wrapper.read(Types.BOOLEAN);\n                    final boolean showRespawnScreen = wrapper.read(Types.BOOLEAN);\n\n                    wrapper.read(Types.BOOLEAN); // Limited crafting\n\n                    final String dimensionType = wrapper.read(Types.STRING);\n                    final String world = wrapper.read(Types.STRING);\n                    final long seed = wrapper.read(Types.LONG);\n\n                    wrapper.passthrough(Types.BYTE); // Gamemode\n                    wrapper.passthrough(Types.BYTE); // Previous gamemode\n\n                    wrapper.write(Types.STRING_ARRAY, worlds);\n                    wrapper.write(Types.NAMED_COMPOUND_TAG, configurationPacketStorage.registry());\n                    wrapper.write(Types.STRING, dimensionType);\n                    wrapper.write(Types.STRING, world);\n                    wrapper.write(Types.LONG, seed);\n                    wrapper.write(Types.VAR_INT, maxPlayers);\n                    wrapper.write(Types.VAR_INT, viewDistance);\n                    wrapper.write(Types.VAR_INT, simulationDistance);\n                    wrapper.write(Types.BOOLEAN, reducedDebugInfo);\n                    wrapper.write(Types.BOOLEAN, showRespawnScreen);\n\n                    worldDataTrackerHandlerByKey().handle(wrapper);\n\n                    wrapper.send(Protocol1_20_2To1_20.class);\n                    wrapper.cancel();\n\n                    if (configurationPacketStorage.enabledFeatures() != null) {\n                        final PacketWrapper featuresPacket = wrapper.create(ClientboundPackets1_19_4.UPDATE_ENABLED_FEATURES);\n                        featuresPacket.write(Types.STRING_ARRAY, configurationPacketStorage.enabledFeatures());\n                        featuresPacket.send(Protocol1_20_2To1_20.class);\n                    }\n\n                    configurationPacketStorage.sendQueuedPackets(wrapper.user());\n                });\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_20_2.RESPAWN, new PacketHandlers() {\n            @Override\n            public void register() {\n                handler(wrapper -> {\n                    wrapper.passthrough(Types.STRING); // Dimension type\n                    wrapper.passthrough(Types.STRING); // World\n                    wrapper.passthrough(Types.LONG); // Seed\n                    wrapper.write(Types.UNSIGNED_BYTE, wrapper.read(Types.BYTE).shortValue()); // Gamemode\n                    wrapper.passthrough(Types.BYTE); // Previous gamemode\n                    wrapper.passthrough(Types.BOOLEAN); // Debug\n                    wrapper.passthrough(Types.BOOLEAN); // Flat\n\n                    final GlobalBlockPosition lastDeathPosition = wrapper.read(Types.OPTIONAL_GLOBAL_POSITION);\n                    final int portalCooldown = wrapper.read(Types.VAR_INT);\n\n                    wrapper.passthrough(Types.BYTE); // Data to keep\n\n                    wrapper.write(Types.OPTIONAL_GLOBAL_POSITION, lastDeathPosition);\n                    wrapper.write(Types.VAR_INT, portalCooldown);\n                });\n                handler(worldDataTrackerHandlerByKey()); // Tracks world height and name for chunk data and entity (un)tracking\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_20_2.UPDATE_MOB_EFFECT, wrapper -> {\n            wrapper.passthrough(Types.VAR_INT); // Entity id\n            wrapper.write(Types.VAR_INT, wrapper.read(Types.VAR_INT) + 1); // Effect id\n            wrapper.passthrough(Types.BYTE); // Amplifier\n            wrapper.passthrough(Types.VAR_INT); // Duration\n            wrapper.passthrough(Types.BYTE); // Flags\n\n            final CompoundTag factorData = wrapper.read(Types.OPTIONAL_COMPOUND_TAG);\n            wrapper.write(Types.OPTIONAL_NAMED_COMPOUND_TAG, factorData); // Factor data\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_20_2.REMOVE_MOB_EFFECT, wrapper -> {\n            wrapper.passthrough(Types.VAR_INT); // Entity id\n            wrapper.write(Types.VAR_INT, wrapper.read(Types.VAR_INT) + 1); // Effect id\n        });\n    }\n\n    @Override\n    protected void registerRewrites() {\n        filter().mapDataType(Types1_20.ENTITY_DATA_TYPES::byId);\n        registerEntityDataTypeHandler(Types1_20.ENTITY_DATA_TYPES.itemType, Types1_20.ENTITY_DATA_TYPES.blockStateType, Types1_20.ENTITY_DATA_TYPES.optionalBlockStateType, Types1_20.ENTITY_DATA_TYPES.particleType, null, null);\n        registerBlockStateHandler(EntityTypes1_19_4.ABSTRACT_MINECART, 11);\n\n        filter().type(EntityTypes1_19_4.DISPLAY).removeIndex(10);\n    }\n\n    @Override\n    public EntityType typeFromId(final int type) {\n        return EntityTypes1_19_4.getTypeFromId(type);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_20_2to1_20/storage/ConfigurationPacketStorage.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_20_2to1_20.storage;\n\nimport com.google.common.base.Preconditions;\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.viabackwards.protocol.v1_20_2to1_20.Protocol1_20_2To1_20;\nimport com.viaversion.viaversion.api.connection.StorableObject;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.protocol.packet.PacketType;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.protocols.v1_19_3to1_19_4.packet.ClientboundPackets1_19_4;\nimport io.netty.buffer.ByteBuf;\nimport io.netty.buffer.Unpooled;\nimport java.util.ArrayList;\nimport java.util.List;\nimport org.checkerframework.checker.nullness.qual.Nullable;\n\npublic final class ConfigurationPacketStorage implements StorableObject {\n\n    private final List<QueuedPacket> rawPackets = new ArrayList<>();\n    private CompoundTag registry;\n    private String[] enabledFeatures;\n    private boolean finished;\n    private QueuedPacket resourcePack;\n\n    public void setResourcePack(final PacketWrapper wrapper) {\n        resourcePack = toQueuedPacket(wrapper, ClientboundPackets1_19_4.RESOURCE_PACK);\n    }\n\n    public CompoundTag registry() {\n        Preconditions.checkNotNull(registry);\n        return registry;\n    }\n\n    public void setRegistry(final CompoundTag registry) {\n        this.registry = registry;\n    }\n\n    public String @Nullable [] enabledFeatures() {\n        return enabledFeatures;\n    }\n\n    public void setEnabledFeatures(final String[] enabledFeatures) {\n        this.enabledFeatures = enabledFeatures;\n    }\n\n    public void addRawPacket(final PacketWrapper wrapper, final PacketType type) {\n        rawPackets.add(toQueuedPacket(wrapper, type));\n    }\n\n    private QueuedPacket toQueuedPacket(final PacketWrapper wrapper, final PacketType type) {\n        Preconditions.checkArgument(!wrapper.isCancelled(), \"Wrapper should be cancelled AFTER calling toQueuedPacket\");\n\n        // It's easier to just copy it to a byte array buffer than to manually read the data\n        final ByteBuf buf = Unpooled.buffer();\n        //noinspection deprecation\n        wrapper.setId(-1); // Don't write the packet id to the buffer\n        wrapper.writeToBuffer(buf);\n        return new QueuedPacket(buf, type);\n    }\n\n    public void sendQueuedPackets(final UserConnection connection) {\n        // Send resource pack at the end\n        List<QueuedPacket> packets = rawPackets;\n        if (resourcePack != null) {\n            packets = new ArrayList<>(rawPackets);\n            packets.add(resourcePack);\n            resourcePack = null;\n        }\n\n        for (final QueuedPacket queuedPacket : packets) {\n            // Don't clear the list or use the original buffer, we might need them later if a server skips subsequent config phases\n            final ByteBuf buf = queuedPacket.buf().copy();\n            try {\n                final PacketWrapper packet = PacketWrapper.create(queuedPacket.packetType(), buf, connection);\n                packet.send(Protocol1_20_2To1_20.class);\n            } finally {\n                buf.release();\n            }\n        }\n    }\n\n    public boolean isFinished() {\n        return finished;\n    }\n\n    public void setFinished(final boolean finished) {\n        this.finished = finished;\n    }\n\n    public record QueuedPacket(ByteBuf buf, PacketType packetType) {\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_20_3to1_20_2/Protocol1_20_3To1_20_2.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_20_3to1_20_2;\n\nimport com.viaversion.nbt.tag.Tag;\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viabackwards.api.data.BackwardsMappingData;\nimport com.viaversion.viabackwards.api.rewriters.text.JsonNBTComponentRewriter;\nimport com.viaversion.viabackwards.protocol.v1_20_3to1_20_2.rewriter.BlockItemPacketRewriter1_20_3;\nimport com.viaversion.viabackwards.protocol.v1_20_3to1_20_2.rewriter.BlockPacketRewriter1_20_3;\nimport com.viaversion.viabackwards.protocol.v1_20_3to1_20_2.rewriter.EntityPacketRewriter1_20_3;\nimport com.viaversion.viabackwards.protocol.v1_20_3to1_20_2.storage.ResourcepackIDStorage;\nimport com.viaversion.viabackwards.protocol.v1_20_3to1_20_2.storage.SpawnPositionStorage;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.BlockPosition;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_20_3;\nimport com.viaversion.viaversion.api.minecraft.item.Item;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.protocol.packet.provider.PacketTypesProvider;\nimport com.viaversion.viaversion.api.protocol.packet.provider.SimplePacketTypesProvider;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandler;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.data.entity.EntityTrackerBase;\nimport com.viaversion.viaversion.libs.fastutil.Pair;\nimport com.viaversion.viaversion.protocols.v1_19_3to1_19_4.rewriter.CommandRewriter1_19_4;\nimport com.viaversion.viaversion.protocols.v1_20_2to1_20_3.Protocol1_20_2To1_20_3;\nimport com.viaversion.viaversion.protocols.v1_20_2to1_20_3.packet.ClientboundConfigurationPackets1_20_3;\nimport com.viaversion.viaversion.protocols.v1_20_2to1_20_3.packet.ClientboundPacket1_20_3;\nimport com.viaversion.viaversion.protocols.v1_20_2to1_20_3.packet.ClientboundPackets1_20_3;\nimport com.viaversion.viaversion.protocols.v1_20_2to1_20_3.packet.ServerboundPacket1_20_3;\nimport com.viaversion.viaversion.protocols.v1_20_2to1_20_3.packet.ServerboundPackets1_20_3;\nimport com.viaversion.viaversion.protocols.v1_20to1_20_2.packet.ClientboundConfigurationPackets1_20_2;\nimport com.viaversion.viaversion.protocols.v1_20to1_20_2.packet.ClientboundPacket1_20_2;\nimport com.viaversion.viaversion.protocols.v1_20to1_20_2.packet.ClientboundPackets1_20_2;\nimport com.viaversion.viaversion.protocols.v1_20to1_20_2.packet.ServerboundConfigurationPackets1_20_2;\nimport com.viaversion.viaversion.protocols.v1_20to1_20_2.packet.ServerboundPacket1_20_2;\nimport com.viaversion.viaversion.protocols.v1_20to1_20_2.packet.ServerboundPackets1_20_2;\nimport com.viaversion.viaversion.rewriter.BlockRewriter;\nimport com.viaversion.viaversion.rewriter.ParticleRewriter;\nimport com.viaversion.viaversion.rewriter.TagRewriter;\nimport com.viaversion.viaversion.rewriter.text.ComponentRewriterBase;\nimport com.viaversion.viaversion.util.ComponentUtil;\nimport java.util.BitSet;\nimport java.util.UUID;\n\nimport static com.viaversion.viaversion.util.ProtocolUtil.packetTypeMap;\n\npublic final class Protocol1_20_3To1_20_2 extends BackwardsProtocol<ClientboundPacket1_20_3, ClientboundPacket1_20_2, ServerboundPacket1_20_3, ServerboundPacket1_20_2> {\n\n    public static final BackwardsMappingData MAPPINGS = new BackwardsMappingData(\"1.20.3\", \"1.20.2\", Protocol1_20_2To1_20_3.class);\n    private final EntityPacketRewriter1_20_3 entityRewriter = new EntityPacketRewriter1_20_3(this);\n    private final BlockItemPacketRewriter1_20_3 itemRewriter = new BlockItemPacketRewriter1_20_3(this);\n    private final ParticleRewriter<ClientboundPacket1_20_3> particleRewriter = new ParticleRewriter<>(this);\n    private final JsonNBTComponentRewriter<ClientboundPacket1_20_3> translatableRewriter = new JsonNBTComponentRewriter<>(this, ComponentRewriterBase.ReadType.NBT);\n    private final TagRewriter<ClientboundPacket1_20_3> tagRewriter = new TagRewriter<>(this);\n    private final BlockRewriter<ClientboundPacket1_20_3> blockRewriter = new BlockPacketRewriter1_20_3(this);\n\n    public Protocol1_20_3To1_20_2() {\n        super(ClientboundPacket1_20_3.class, ClientboundPacket1_20_2.class, ServerboundPacket1_20_3.class, ServerboundPacket1_20_2.class);\n    }\n\n    @Override\n    protected void registerPackets() {\n        super.registerPackets();\n\n        final CommandRewriter1_19_4<ClientboundPacket1_20_3> commandRewriter = new CommandRewriter1_19_4<>(this) {\n            @Override\n            public void handleArgument(final PacketWrapper wrapper, final String argumentType) {\n                if (argumentType.equals(\"minecraft:style\")) {\n                    wrapper.write(Types.VAR_INT, 1); // Phrase\n                } else {\n                    super.handleArgument(wrapper, argumentType);\n                }\n            }\n        };\n        replaceClientbound(ClientboundPackets1_20_3.COMMANDS, commandRewriter::handle1_19);\n\n        registerClientbound(ClientboundPackets1_20_3.RESET_SCORE, ClientboundPackets1_20_2.SET_SCORE, wrapper -> {\n            wrapper.passthrough(Types.STRING); // Owner\n            wrapper.write(Types.VAR_INT, 1); // Reset score\n\n            final String objectiveName = wrapper.read(Types.OPTIONAL_STRING);\n            wrapper.write(Types.STRING, objectiveName != null ? objectiveName : \"\"); // Objective name\n        });\n        replaceClientbound(ClientboundPackets1_20_3.SET_SCORE, wrapper -> {\n            wrapper.passthrough(Types.STRING); // Owner\n            wrapper.write(Types.VAR_INT, 0); // Change score\n            wrapper.passthrough(Types.STRING); // Objective name\n            wrapper.passthrough(Types.VAR_INT); // Score\n\n            // Remove display and number format\n            wrapper.clearInputBuffer();\n        });\n        replaceClientbound(ClientboundPackets1_20_3.SET_OBJECTIVE, wrapper -> {\n            wrapper.passthrough(Types.STRING); // Objective Name\n            final byte action = wrapper.passthrough(Types.BYTE); // Method\n            if (action == 0 || action == 2) {\n                convertComponent(wrapper); // Display Name\n                wrapper.passthrough(Types.VAR_INT); // Render type\n\n                // Remove number format\n                wrapper.clearInputBuffer();\n            }\n        });\n\n        cancelClientbound(ClientboundPackets1_20_3.TICKING_STATE);\n        cancelClientbound(ClientboundPackets1_20_3.TICKING_STEP);\n\n        registerServerbound(ServerboundPackets1_20_2.SET_JIGSAW_BLOCK, wrapper -> {\n            wrapper.passthrough(Types.BLOCK_POSITION1_14); // Position\n            wrapper.passthrough(Types.STRING); // Name\n            wrapper.passthrough(Types.STRING); // Target\n            wrapper.passthrough(Types.STRING); // Pool\n            wrapper.passthrough(Types.STRING); // Final state\n            wrapper.passthrough(Types.STRING); // Joint type\n            wrapper.write(Types.VAR_INT, 0); // Selection priority\n            wrapper.write(Types.VAR_INT, 0); // Placement priority\n        });\n\n        // Components are now (mostly) written as nbt instead of json strings\n        replaceClientbound(ClientboundPackets1_20_3.UPDATE_ADVANCEMENTS, wrapper -> {\n            wrapper.passthrough(Types.BOOLEAN); // Reset/clear\n            final int size = wrapper.passthrough(Types.VAR_INT); // Mapping size\n            for (int i = 0; i < size; i++) {\n                wrapper.passthrough(Types.STRING); // Identifier\n                wrapper.passthrough(Types.OPTIONAL_STRING); // Parent\n\n                // Display data\n                if (wrapper.passthrough(Types.BOOLEAN)) {\n                    convertComponent(wrapper); // Title\n                    convertComponent(wrapper); // Description\n                    final Item icon = itemRewriter.handleItemToClient(wrapper.user(), wrapper.read(Types.ITEM1_20_2));\n                    wrapper.write(Types.ITEM1_20_2, icon);\n                    wrapper.passthrough(Types.VAR_INT); // Frame type\n                    final int flags = wrapper.passthrough(Types.INT);\n                    if ((flags & 1) != 0) {\n                        wrapper.passthrough(Types.STRING); // Background texture\n                    }\n                    wrapper.passthrough(Types.FLOAT); // X\n                    wrapper.passthrough(Types.FLOAT); // Y\n                }\n\n                final int requirements = wrapper.passthrough(Types.VAR_INT);\n                for (int array = 0; array < requirements; array++) {\n                    wrapper.passthrough(Types.STRING_ARRAY);\n                }\n\n                wrapper.passthrough(Types.BOOLEAN); // Send telemetry\n            }\n        });\n        registerClientbound(ClientboundPackets1_20_3.COMMAND_SUGGESTIONS, wrapper -> {\n            wrapper.passthrough(Types.VAR_INT); // Transaction id\n            wrapper.passthrough(Types.VAR_INT); // Start\n            wrapper.passthrough(Types.VAR_INT); // Length\n\n            final int suggestions = wrapper.passthrough(Types.VAR_INT);\n            for (int i = 0; i < suggestions; i++) {\n                wrapper.passthrough(Types.STRING); // Suggestion\n                convertOptionalComponent(wrapper); // Tooltip\n            }\n        });\n        registerClientbound(ClientboundPackets1_20_3.MAP_ITEM_DATA, wrapper -> {\n            wrapper.passthrough(Types.VAR_INT); // Map id\n            wrapper.passthrough(Types.BYTE); // Scale\n            wrapper.passthrough(Types.BOOLEAN); // Locked\n            if (wrapper.passthrough(Types.BOOLEAN)) {\n                final int icons = wrapper.passthrough(Types.VAR_INT);\n                for (int i = 0; i < icons; i++) {\n                    wrapper.passthrough(Types.VAR_INT); // Type\n                    wrapper.passthrough(Types.BYTE); // X\n                    wrapper.passthrough(Types.BYTE); // Y\n                    wrapper.passthrough(Types.BYTE); // Rotation\n                    convertOptionalComponent(wrapper); // Display name\n                }\n            }\n        });\n        replaceClientbound(ClientboundPackets1_20_3.BOSS_EVENT, wrapper -> {\n            wrapper.passthrough(Types.UUID); // Id\n\n            final int action = wrapper.passthrough(Types.VAR_INT);\n            if (action == 0 || action == 3) {\n                convertComponent(wrapper);\n            }\n        });\n        replaceClientbound(ClientboundPackets1_20_3.PLAYER_CHAT, wrapper -> {\n            wrapper.passthrough(Types.UUID); // Sender\n            wrapper.passthrough(Types.VAR_INT); // Index\n            wrapper.passthrough(Types.OPTIONAL_SIGNATURE_BYTES); // Signature\n            wrapper.passthrough(Types.STRING); // Plain content\n            wrapper.passthrough(Types.LONG); // Timestamp\n            wrapper.passthrough(Types.LONG); // Salt\n\n            final int lastSeen = wrapper.passthrough(Types.VAR_INT);\n            for (int i = 0; i < lastSeen; i++) {\n                final int index = wrapper.passthrough(Types.VAR_INT);\n                if (index == 0) {\n                    wrapper.passthrough(Types.SIGNATURE_BYTES);\n                }\n            }\n\n            convertOptionalComponent(wrapper); // Unsigned content\n\n            final int filterMaskType = wrapper.passthrough(Types.VAR_INT);\n            if (filterMaskType == 2) {\n                wrapper.passthrough(Types.LONG_ARRAY_PRIMITIVE); // Mask\n            }\n\n            wrapper.passthrough(Types.VAR_INT); // Chat type\n            convertComponent(wrapper); // Sender\n            convertOptionalComponent(wrapper); // Target\n        });\n        replaceClientbound(ClientboundPackets1_20_3.SET_PLAYER_TEAM, wrapper -> {\n            wrapper.passthrough(Types.STRING); // Team Name\n            final byte action = wrapper.passthrough(Types.BYTE); // Mode\n            if (action == 0 || action == 2) {\n                convertComponent(wrapper); // Display Name\n                wrapper.passthrough(Types.BYTE); // Flags\n                wrapper.passthrough(Types.STRING); // Name Tag Visibility\n                wrapper.passthrough(Types.STRING); // Collision rule\n                wrapper.passthrough(Types.VAR_INT); // Color\n                convertComponent(wrapper); // Prefix\n                convertComponent(wrapper); // Suffix\n            }\n        });\n\n        replaceClientbound(ClientboundConfigurationPackets1_20_3.DISCONNECT, this::convertComponent);\n        replaceClientbound(ClientboundPackets1_20_3.DISCONNECT, this::convertComponent);\n        registerClientbound(ClientboundPackets1_20_3.RESOURCE_PACK_PUSH, ClientboundPackets1_20_2.RESOURCE_PACK, resourcePackHandler());\n        replaceClientbound(ClientboundPackets1_20_3.SERVER_DATA, this::convertComponent);\n        replaceClientbound(ClientboundPackets1_20_3.SET_ACTION_BAR_TEXT, this::convertComponent);\n        replaceClientbound(ClientboundPackets1_20_3.SET_TITLE_TEXT, this::convertComponent);\n        replaceClientbound(ClientboundPackets1_20_3.SET_SUBTITLE_TEXT, this::convertComponent);\n        replaceClientbound(ClientboundPackets1_20_3.DISGUISED_CHAT, wrapper -> {\n            convertComponent(wrapper);\n            wrapper.passthrough(Types.VAR_INT); // Chat type\n            convertComponent(wrapper); // Name\n            convertOptionalComponent(wrapper); // Target name\n        });\n        replaceClientbound(ClientboundPackets1_20_3.SYSTEM_CHAT, this::convertComponent);\n        replaceClientbound(ClientboundPackets1_20_3.OPEN_SCREEN, wrapper -> {\n            wrapper.passthrough(Types.VAR_INT); // Container id\n\n            final int containerTypeId = wrapper.read(Types.VAR_INT);\n            final int mappedContainerTypeId = MAPPINGS.getMenuMappings().getNewId(containerTypeId);\n            if (mappedContainerTypeId == -1) {\n                wrapper.cancel();\n                return;\n            }\n\n            wrapper.write(Types.VAR_INT, mappedContainerTypeId);\n\n            convertComponent(wrapper);\n        });\n        replaceClientbound(ClientboundPackets1_20_3.TAB_LIST, wrapper -> {\n            convertComponent(wrapper);\n            convertComponent(wrapper);\n        });\n\n        replaceClientbound(ClientboundPackets1_20_3.PLAYER_COMBAT_KILL, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT); // Duration\n                handler(wrapper -> convertComponent(wrapper));\n            }\n        });\n        replaceClientbound(ClientboundPackets1_20_3.PLAYER_INFO_UPDATE, wrapper -> {\n            final BitSet actions = wrapper.passthrough(Types.PROFILE_ACTIONS_ENUM1_19_3);\n            final int entries = wrapper.passthrough(Types.VAR_INT);\n            for (int i = 0; i < entries; i++) {\n                wrapper.passthrough(Types.UUID);\n                if (actions.get(0)) {\n                    wrapper.passthrough(Types.STRING); // Player Name\n                    wrapper.passthrough(Types.PROFILE_PROPERTY_ARRAY);\n                }\n                if (actions.get(1) && wrapper.passthrough(Types.BOOLEAN)) {\n                    wrapper.passthrough(Types.UUID); // Session UUID\n                    wrapper.passthrough(Types.PROFILE_KEY);\n                }\n                if (actions.get(2)) {\n                    wrapper.passthrough(Types.VAR_INT); // Gamemode\n                }\n                if (actions.get(3)) {\n                    wrapper.passthrough(Types.BOOLEAN); // Listed\n                }\n                if (actions.get(4)) {\n                    wrapper.passthrough(Types.VAR_INT); // Latency\n                }\n                if (actions.get(5)) {\n                    convertOptionalComponent(wrapper); // Display name\n                }\n            }\n        });\n        registerClientbound(ClientboundPackets1_20_3.SET_DEFAULT_SPAWN_POSITION, wrapper -> {\n            final BlockPosition position = wrapper.passthrough(Types.BLOCK_POSITION1_14);\n            final float angle = wrapper.passthrough(Types.FLOAT);\n\n            wrapper.user().get(SpawnPositionStorage.class).setSpawnPosition(Pair.of(position, angle));\n        });\n        registerClientbound(ClientboundPackets1_20_3.GAME_EVENT, wrapper -> {\n            final short reason = wrapper.passthrough(Types.UNSIGNED_BYTE);\n\n            if (reason == 13) { // Level chunks load start\n                wrapper.cancel();\n                final Pair<BlockPosition, Float> spawnPositionAndAngle = wrapper.user().get(SpawnPositionStorage.class).getSpawnPosition();\n\n                // To emulate the old behavior, we send a fake spawn pos packet containing the actual spawn pos which forces\n                // the 1.20.2 client to close the downloading terrain screen like the new game state does\n                final PacketWrapper spawnPosition = wrapper.create(ClientboundPackets1_20_2.SET_DEFAULT_SPAWN_POSITION);\n                spawnPosition.write(Types.BLOCK_POSITION1_14, spawnPositionAndAngle.first()); // position\n                spawnPosition.write(Types.FLOAT, spawnPositionAndAngle.second()); // angle\n                spawnPosition.send(Protocol1_20_3To1_20_2.class, true);\n            }\n        });\n\n        cancelClientbound(ClientboundPackets1_20_3.RESOURCE_PACK_POP);\n        registerServerbound(ServerboundPackets1_20_2.RESOURCE_PACK, resourcePackStatusHandler());\n\n        cancelClientbound(ClientboundConfigurationPackets1_20_3.RESOURCE_PACK_POP);\n        registerServerbound(ServerboundConfigurationPackets1_20_2.RESOURCE_PACK, resourcePackStatusHandler());\n        registerClientbound(ClientboundConfigurationPackets1_20_3.RESOURCE_PACK_PUSH, ClientboundConfigurationPackets1_20_2.RESOURCE_PACK, resourcePackHandler());\n    }\n\n    private PacketHandler resourcePackStatusHandler() {\n        return wrapper -> {\n            final ResourcepackIDStorage storage = wrapper.user().get(ResourcepackIDStorage.class);\n            wrapper.write(Types.UUID, storage != null ? storage.uuid() : UUID.randomUUID());\n        };\n    }\n\n    private PacketHandler resourcePackHandler() {\n        return wrapper -> {\n            final UUID uuid = wrapper.read(Types.UUID);\n            wrapper.user().put(new ResourcepackIDStorage(uuid));\n\n            wrapper.passthrough(Types.STRING); // Url\n            wrapper.passthrough(Types.STRING); // Hash\n            wrapper.passthrough(Types.BOOLEAN); // Required\n            convertOptionalComponent(wrapper);\n        };\n    }\n\n    private void convertComponent(final PacketWrapper wrapper) {\n        final Tag tag = wrapper.read(Types.TRUSTED_TAG);\n        translatableRewriter.processTag(wrapper.user(), tag);\n        wrapper.write(Types.COMPONENT, ComponentUtil.tagToJson(tag));\n    }\n\n    private void convertOptionalComponent(final PacketWrapper wrapper) {\n        final Tag tag = wrapper.read(Types.TRUSTED_OPTIONAL_TAG);\n        translatableRewriter.processTag(wrapper.user(), tag);\n        wrapper.write(Types.OPTIONAL_COMPONENT, ComponentUtil.tagToJson(tag));\n    }\n\n    @Override\n    public void init(final UserConnection connection) {\n        connection.put(new SpawnPositionStorage());\n        addEntityTracker(connection, new EntityTrackerBase(connection, EntityTypes1_20_3.PLAYER));\n    }\n\n    @Override\n    public BackwardsMappingData getMappingData() {\n        return MAPPINGS;\n    }\n\n    @Override\n    public BlockItemPacketRewriter1_20_3 getItemRewriter() {\n        return itemRewriter;\n    }\n\n    @Override\n    public BlockRewriter<ClientboundPacket1_20_3> getBlockRewriter() {\n        return blockRewriter;\n    }\n\n    @Override\n    public ParticleRewriter<ClientboundPacket1_20_3> getParticleRewriter() {\n        return particleRewriter;\n    }\n\n    @Override\n    public EntityPacketRewriter1_20_3 getEntityRewriter() {\n        return entityRewriter;\n    }\n\n    @Override\n    public JsonNBTComponentRewriter<ClientboundPacket1_20_3> getComponentRewriter() {\n        return translatableRewriter;\n    }\n\n    @Override\n    public TagRewriter<ClientboundPacket1_20_3> getTagRewriter() {\n        return tagRewriter;\n    }\n\n    @Override\n    protected PacketTypesProvider<ClientboundPacket1_20_3, ClientboundPacket1_20_2, ServerboundPacket1_20_3, ServerboundPacket1_20_2> createPacketTypesProvider() {\n        return new SimplePacketTypesProvider<>(\n            packetTypeMap(unmappedClientboundPacketType, ClientboundPackets1_20_3.class, ClientboundConfigurationPackets1_20_3.class),\n            packetTypeMap(mappedClientboundPacketType, ClientboundPackets1_20_2.class, ClientboundConfigurationPackets1_20_2.class),\n            packetTypeMap(mappedServerboundPacketType, ServerboundPackets1_20_3.class, ServerboundConfigurationPackets1_20_2.class),\n            packetTypeMap(unmappedServerboundPacketType, ServerboundPackets1_20_2.class, ServerboundConfigurationPackets1_20_2.class)\n        );\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_20_3to1_20_2/rewriter/BlockItemPacketRewriter1_20_3.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_20_3to1_20_2.rewriter;\n\nimport com.viaversion.viabackwards.api.rewriters.BackwardsItemRewriter;\nimport com.viaversion.viabackwards.protocol.v1_20_3to1_20_2.Protocol1_20_3To1_20_2;\nimport com.viaversion.viaversion.api.data.ParticleMappings;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.version.Types1_20_3;\nimport com.viaversion.viaversion.protocols.v1_20_2to1_20_3.packet.ClientboundPacket1_20_3;\nimport com.viaversion.viaversion.protocols.v1_20_2to1_20_3.packet.ClientboundPackets1_20_3;\nimport com.viaversion.viaversion.protocols.v1_20_2to1_20_3.rewriter.RecipeRewriter1_20_3;\nimport com.viaversion.viaversion.protocols.v1_20to1_20_2.packet.ServerboundPacket1_20_2;\n\npublic final class BlockItemPacketRewriter1_20_3 extends BackwardsItemRewriter<ClientboundPacket1_20_3, ServerboundPacket1_20_2, Protocol1_20_3To1_20_2> {\n\n    public BlockItemPacketRewriter1_20_3(final Protocol1_20_3To1_20_2 protocol) {\n        super(protocol, Types.ITEM1_20_2, Types.ITEM1_20_2_ARRAY);\n    }\n\n    @Override\n    public void registerPackets() {\n        protocol.replaceClientbound(ClientboundPackets1_20_3.LEVEL_PARTICLES, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT); // 0 - Particle ID\n                map(Types.BOOLEAN); // 1 - Long Distance\n                map(Types.DOUBLE); // 2 - X\n                map(Types.DOUBLE); // 3 - Y\n                map(Types.DOUBLE); // 4 - Z\n                map(Types.FLOAT); // 5 - Offset X\n                map(Types.FLOAT); // 6 - Offset Y\n                map(Types.FLOAT); // 7 - Offset Z\n                map(Types.FLOAT); // 8 - Particle Data\n                map(Types.INT); // 9 - Particle Count\n                handler(wrapper -> {\n                    final int id = wrapper.get(Types.VAR_INT, 0);\n                    final ParticleMappings particleMappings = protocol.getMappingData().getParticleMappings();\n                    if (id == particleMappings.id(\"vibration\")) {\n                        final int positionSourceType = wrapper.read(Types.VAR_INT);\n                        if (positionSourceType == 0) {\n                            wrapper.write(Types.STRING, \"minecraft:block\");\n                        } else if (positionSourceType == 1) {\n                            wrapper.write(Types.STRING, \"minecraft:entity\");\n                        } else {\n                            protocol.getLogger().warning(\"Unknown position source type: \" + positionSourceType);\n                            wrapper.cancel();\n                        }\n                    }\n                });\n                handler(protocol.getParticleRewriter().levelParticlesHandler1_13(Types.VAR_INT));\n            }\n        });\n\n        new RecipeRewriter1_20_3<>(protocol) {\n            @Override\n            public void handleCraftingShaped(final PacketWrapper wrapper) {\n                // Move width and height up\n                final String group = wrapper.read(Types.STRING);\n                final int craftingBookCategory = wrapper.read(Types.VAR_INT);\n\n                final int width = wrapper.passthrough(Types.VAR_INT);\n                final int height = wrapper.passthrough(Types.VAR_INT);\n\n                wrapper.write(Types.STRING, group);\n                wrapper.write(Types.VAR_INT, craftingBookCategory);\n\n                final int ingredients = height * width;\n                for (int i = 0; i < ingredients; i++) {\n                    handleIngredient(wrapper);\n                }\n                rewrite(wrapper.user(), wrapper.passthrough(itemType())); // Result\n                wrapper.passthrough(Types.BOOLEAN); // Show notification\n            }\n        }.register(ClientboundPackets1_20_3.UPDATE_RECIPES);\n\n        protocol.registerClientbound(ClientboundPackets1_20_3.EXPLODE, wrapper -> {\n            wrapper.passthrough(Types.DOUBLE); // X\n            wrapper.passthrough(Types.DOUBLE); // Y\n            wrapper.passthrough(Types.DOUBLE); // Z\n            wrapper.passthrough(Types.FLOAT); // Power\n\n            final int blocks = wrapper.read(Types.VAR_INT);\n            final byte[][] toBlow = new byte[blocks][3];\n            for (int i = 0; i < blocks; i++) {\n                toBlow[i] = new byte[]{\n                    wrapper.read(Types.BYTE), // Relative X\n                    wrapper.read(Types.BYTE), // Relative Y\n                    wrapper.read(Types.BYTE) // Relative Z\n                };\n            }\n\n            final float knockbackX = wrapper.read(Types.FLOAT); // Knockback X\n            final float knockbackY = wrapper.read(Types.FLOAT); // Knockback Y\n            final float knockbackZ = wrapper.read(Types.FLOAT); // Knockback Z\n\n            final int blockInteraction = wrapper.read(Types.VAR_INT); // Block interaction type\n            // 0 = keep, 1 = destroy, 2 = destroy_with_decay, 3 = trigger_block\n            if (blockInteraction == 1 || blockInteraction == 2) {\n                wrapper.write(Types.VAR_INT, blocks);\n                for (final byte[] relativeXYZ : toBlow) {\n                    wrapper.write(Types.BYTE, relativeXYZ[0]);\n                    wrapper.write(Types.BYTE, relativeXYZ[1]);\n                    wrapper.write(Types.BYTE, relativeXYZ[2]);\n                }\n            } else {\n                // Explosion doesn't destroy blocks\n                wrapper.write(Types.VAR_INT, 0);\n            }\n\n            wrapper.write(Types.FLOAT, knockbackX);\n            wrapper.write(Types.FLOAT, knockbackY);\n            wrapper.write(Types.FLOAT, knockbackZ);\n\n            // TODO Probably needs handling\n            wrapper.read(Types1_20_3.PARTICLE); // Small explosion particle\n            wrapper.read(Types1_20_3.PARTICLE); // Large explosion particle\n            wrapper.read(Types.STRING); // Explosion sound\n            wrapper.read(Types.OPTIONAL_FLOAT); // Sound range\n        });\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_20_3to1_20_2/rewriter/BlockPacketRewriter1_20_3.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_20_3to1_20_2.rewriter;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.StringTag;\nimport com.viaversion.viabackwards.protocol.v1_20_3to1_20_2.Protocol1_20_3To1_20_2;\nimport com.viaversion.viaversion.api.Via;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.blockentity.BlockEntity;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.chunk.ChunkType1_20_2;\nimport com.viaversion.viaversion.libs.gson.JsonElement;\nimport com.viaversion.viaversion.protocols.v1_20_2to1_20_3.packet.ClientboundPacket1_20_3;\nimport com.viaversion.viaversion.rewriter.BlockRewriter;\nimport com.viaversion.viaversion.util.ComponentUtil;\nimport com.viaversion.viaversion.util.SerializerVersion;\nimport com.viaversion.viaversion.util.StringUtil;\nimport java.util.logging.Level;\n\npublic final class BlockPacketRewriter1_20_3 extends BlockRewriter<ClientboundPacket1_20_3> {\n\n    public BlockPacketRewriter1_20_3(final Protocol1_20_3To1_20_2 protocol) {\n        super(protocol, Types.BLOCK_POSITION1_14, Types.COMPOUND_TAG, ChunkType1_20_2::new, null);\n    }\n\n    @Override\n    public void handleBlockEntity(final UserConnection connection, final BlockEntity blockEntity) {\n        final CompoundTag tag = blockEntity.tag();\n        if (tag == null) {\n            return;\n        }\n\n        final StringTag customName = tag.getStringTag(\"CustomName\");\n        if (customName == null) {\n            return;\n        }\n\n        try {\n            final JsonElement updatedComponent = ComponentUtil.convertJson(customName.getValue(), SerializerVersion.V1_20_3, SerializerVersion.V1_19_4);\n            customName.setValue(updatedComponent.toString());\n        } catch (final Exception e) {\n            if (Via.getConfig().logTextComponentConversionErrors()) {\n                protocol.getLogger().log(Level.SEVERE, \"Error during custom name conversion: \" + StringUtil.forLogging(customName.getValue()), e);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_20_3to1_20_2/rewriter/EntityPacketRewriter1_20_3.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_20_3to1_20_2.rewriter;\n\nimport com.viaversion.viabackwards.api.rewriters.EntityRewriter;\nimport com.viaversion.viabackwards.protocol.v1_20_3to1_20_2.Protocol1_20_3To1_20_2;\nimport com.viaversion.viabackwards.protocol.v1_20_3to1_20_2.storage.SpawnPositionStorage;\nimport com.viaversion.viaversion.api.data.ParticleMappings;\nimport com.viaversion.viaversion.api.minecraft.Particle;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityType;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_20_3;\nimport com.viaversion.viaversion.api.minecraft.entitydata.EntityDataType;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandler;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.version.Types1_20_2;\nimport com.viaversion.viaversion.api.type.types.version.Types1_20_3;\nimport com.viaversion.viaversion.protocols.v1_20_2to1_20_3.packet.ClientboundConfigurationPackets1_20_3;\nimport com.viaversion.viaversion.protocols.v1_20_2to1_20_3.packet.ClientboundPacket1_20_3;\nimport com.viaversion.viaversion.protocols.v1_20_2to1_20_3.packet.ClientboundPackets1_20_3;\nimport com.viaversion.viaversion.util.ComponentUtil;\n\npublic final class EntityPacketRewriter1_20_3 extends EntityRewriter<ClientboundPacket1_20_3, Protocol1_20_3To1_20_2> {\n\n    public EntityPacketRewriter1_20_3(final Protocol1_20_3To1_20_2 protocol) {\n        super(protocol, Types1_20_2.ENTITY_DATA_TYPES.optionalComponentType, Types1_20_2.ENTITY_DATA_TYPES.booleanType);\n    }\n\n    @Override\n    public void registerPackets() {\n        registerSetEntityData(ClientboundPackets1_20_3.SET_ENTITY_DATA, Types1_20_3.ENTITY_DATA_LIST, Types1_20_2.ENTITY_DATA_LIST);\n\n        protocol.registerClientbound(ClientboundConfigurationPackets1_20_3.REGISTRY_DATA, new PacketHandlers() {\n            @Override\n            protected void register() {\n                map(Types.COMPOUND_TAG); // Registry data\n                handler(configurationDimensionDataHandler());\n                handler(configurationBiomeSizeTracker());\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_20_3.LOGIN, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.INT); // Entity id\n                map(Types.BOOLEAN); // Hardcore\n                map(Types.STRING_ARRAY); // World List\n                map(Types.VAR_INT); // Max players\n                map(Types.VAR_INT); // View distance\n                map(Types.VAR_INT); // Simulation distance\n                map(Types.BOOLEAN); // Reduced debug info\n                map(Types.BOOLEAN); // Show death screen\n                map(Types.BOOLEAN); // Limited crafting\n                map(Types.STRING); // Dimension key\n                map(Types.STRING); // World\n\n                handler(spawnPositionHandler());\n                handler(worldDataTrackerHandlerByKey());\n            }\n        });\n        protocol.registerClientbound(ClientboundPackets1_20_3.RESPAWN, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.STRING); // Dimension\n                map(Types.STRING); // World\n\n                handler(spawnPositionHandler());\n                handler(worldDataTrackerHandlerByKey());\n            }\n        });\n    }\n\n    private PacketHandler spawnPositionHandler() {\n        return wrapper -> {\n            final String world = wrapper.get(Types.STRING, 1);\n            wrapper.user().get(SpawnPositionStorage.class).setDimension(world);\n        };\n    }\n\n    @Override\n    protected void registerRewrites() {\n        filter().handler((event, data) -> {\n            final EntityDataType type = data.dataType();\n            if (type == Types1_20_3.ENTITY_DATA_TYPES.componentType) {\n                data.setTypeAndValue(Types1_20_2.ENTITY_DATA_TYPES.componentType, ComponentUtil.tagToJson(data.value()));\n                return;\n            } else if (type == Types1_20_3.ENTITY_DATA_TYPES.optionalComponentType) {\n                data.setTypeAndValue(Types1_20_2.ENTITY_DATA_TYPES.optionalComponentType, ComponentUtil.tagToJson(data.value()));\n                return;\n            } else if (type == Types1_20_3.ENTITY_DATA_TYPES.particleType) {\n                final Particle particle = (Particle) data.getValue();\n                final ParticleMappings particleMappings = protocol.getMappingData().getParticleMappings();\n                if (particle.id() == particleMappings.id(\"vibration\")) {\n                    // Change the type of the position source type argument\n                    final int positionSourceType = particle.<Integer>removeArgument(0).getValue();\n                    if (positionSourceType == 0) {\n                        particle.add(0, Types.STRING, \"minecraft:block\");\n                    } else { // Entity\n                        particle.add(0, Types.STRING, \"minecraft:entity\");\n                    }\n                }\n            } else if (type == Types1_20_3.ENTITY_DATA_TYPES.poseType) {\n                final int pose = data.value();\n                if (pose >= 15) {\n                    event.cancel();\n                }\n            }\n\n            data.setDataType(Types1_20_2.ENTITY_DATA_TYPES.byId(type.typeId()));\n        });\n\n        registerEntityDataTypeHandler(\n            Types1_20_2.ENTITY_DATA_TYPES.itemType,\n            Types1_20_2.ENTITY_DATA_TYPES.blockStateType,\n            Types1_20_2.ENTITY_DATA_TYPES.optionalBlockStateType,\n            Types1_20_2.ENTITY_DATA_TYPES.particleType,\n            Types1_20_2.ENTITY_DATA_TYPES.componentType,\n            Types1_20_2.ENTITY_DATA_TYPES.optionalComponentType\n        );\n        registerBlockStateHandler(EntityTypes1_20_3.ABSTRACT_MINECART, 11);\n\n        filter().type(EntityTypes1_20_3.TNT).removeIndex(9); // Block state\n    }\n\n    @Override\n    public void onMappingDataLoaded() {\n        super.onMappingDataLoaded();\n        mapEntityTypeWithData(EntityTypes1_20_3.BREEZE, EntityTypes1_20_3.BLAZE).jsonName();\n        mapEntityTypeWithData(EntityTypes1_20_3.WIND_CHARGE, EntityTypes1_20_3.SHULKER_BULLET).jsonName();\n    }\n\n    @Override\n    public EntityType typeFromId(final int type) {\n        return EntityTypes1_20_3.getTypeFromId(type);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_20_3to1_20_2/storage/ResourcepackIDStorage.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_20_3to1_20_2.storage;\n\nimport com.viaversion.viaversion.api.connection.StorableObject;\nimport java.util.UUID;\n\npublic record ResourcepackIDStorage(UUID uuid) implements StorableObject {\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_20_3to1_20_2/storage/SpawnPositionStorage.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_20_3to1_20_2.storage;\n\nimport com.viaversion.viaversion.api.connection.StorableObject;\nimport com.viaversion.viaversion.api.minecraft.BlockPosition;\nimport com.viaversion.viaversion.libs.fastutil.Pair;\nimport java.util.Objects;\n\npublic class SpawnPositionStorage implements StorableObject {\n    public static final Pair<BlockPosition, Float> DEFAULT_SPAWN_POSITION = Pair.of(new BlockPosition(8, 64, 8), 0.0F); // Default values copied from the original client\n\n    private Pair<BlockPosition, Float> spawnPosition;\n    private String dimension;\n\n    public Pair<BlockPosition, Float> getSpawnPosition() {\n        return spawnPosition;\n    }\n\n    public void setSpawnPosition(final Pair<BlockPosition, Float> spawnPosition) {\n        this.spawnPosition = spawnPosition;\n    }\n\n    /**\n     * Sets the dimension and resets the spawn position to the default value if the dimension changed.\n     *\n     * @param dimension The new dimension\n     */\n    public void setDimension(final String dimension) {\n        final boolean changed = !Objects.equals(this.dimension, dimension);\n        this.dimension = dimension;\n\n        if (changed) {\n            this.spawnPosition = DEFAULT_SPAWN_POSITION;\n        }\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_20_5to1_20_3/Protocol1_20_5To1_20_3.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_20_5to1_20_3;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viabackwards.api.data.BackwardsMappingData;\nimport com.viaversion.viabackwards.api.rewriters.text.JsonNBTComponentRewriter;\nimport com.viaversion.viabackwards.protocol.v1_20_5to1_20_3.provider.TransferProvider;\nimport com.viaversion.viabackwards.protocol.v1_20_5to1_20_3.rewriter.BlockItemPacketRewriter1_20_5;\nimport com.viaversion.viabackwards.protocol.v1_20_5to1_20_3.rewriter.BlockPacketRewriter1_20_5;\nimport com.viaversion.viabackwards.protocol.v1_20_5to1_20_3.rewriter.ComponentRewriter1_20_5;\nimport com.viaversion.viabackwards.protocol.v1_20_5to1_20_3.rewriter.EntityPacketRewriter1_20_5;\nimport com.viaversion.viabackwards.protocol.v1_20_5to1_20_3.storage.CookieStorage;\nimport com.viaversion.viabackwards.protocol.v1_20_5to1_20_3.storage.RegistryDataStorage;\nimport com.viaversion.viabackwards.protocol.v1_20_5to1_20_3.storage.SecureChatStorage;\nimport com.viaversion.viaversion.api.Via;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.RegistryType;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_20_5;\nimport com.viaversion.viaversion.api.platform.providers.ViaProviders;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.protocol.packet.ServerboundPacketType;\nimport com.viaversion.viaversion.api.protocol.packet.State;\nimport com.viaversion.viaversion.api.protocol.packet.provider.PacketTypesProvider;\nimport com.viaversion.viaversion.api.protocol.packet.provider.SimplePacketTypesProvider;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.version.VersionedTypes;\nimport com.viaversion.viaversion.api.type.types.version.VersionedTypesHolder;\nimport com.viaversion.viaversion.data.entity.EntityTrackerBase;\nimport com.viaversion.viaversion.protocols.base.ClientboundLoginPackets;\nimport com.viaversion.viaversion.protocols.base.ServerboundLoginPackets;\nimport com.viaversion.viaversion.protocols.v1_19_3to1_19_4.rewriter.CommandRewriter1_19_4;\nimport com.viaversion.viaversion.protocols.v1_20_2to1_20_3.packet.ClientboundConfigurationPackets1_20_3;\nimport com.viaversion.viaversion.protocols.v1_20_2to1_20_3.packet.ClientboundPacket1_20_3;\nimport com.viaversion.viaversion.protocols.v1_20_2to1_20_3.packet.ClientboundPackets1_20_3;\nimport com.viaversion.viaversion.protocols.v1_20_2to1_20_3.packet.ServerboundPacket1_20_3;\nimport com.viaversion.viaversion.protocols.v1_20_2to1_20_3.packet.ServerboundPackets1_20_3;\nimport com.viaversion.viaversion.protocols.v1_20_3to1_20_5.Protocol1_20_3To1_20_5;\nimport com.viaversion.viaversion.protocols.v1_20_3to1_20_5.packet.ClientboundConfigurationPackets1_20_5;\nimport com.viaversion.viaversion.protocols.v1_20_3to1_20_5.packet.ClientboundPacket1_20_5;\nimport com.viaversion.viaversion.protocols.v1_20_3to1_20_5.packet.ClientboundPackets1_20_5;\nimport com.viaversion.viaversion.protocols.v1_20_3to1_20_5.packet.ServerboundConfigurationPackets1_20_5;\nimport com.viaversion.viaversion.protocols.v1_20_3to1_20_5.packet.ServerboundPacket1_20_5;\nimport com.viaversion.viaversion.protocols.v1_20_3to1_20_5.packet.ServerboundPackets1_20_5;\nimport com.viaversion.viaversion.protocols.v1_20_3to1_20_5.storage.ArmorTrimStorage;\nimport com.viaversion.viaversion.protocols.v1_20_3to1_20_5.storage.BannerPatternStorage;\nimport com.viaversion.viaversion.protocols.v1_20to1_20_2.packet.ServerboundConfigurationPackets1_20_2;\nimport com.viaversion.viaversion.rewriter.ParticleRewriter;\nimport com.viaversion.viaversion.rewriter.TagRewriter;\n\nimport static com.viaversion.viaversion.util.ProtocolUtil.packetTypeMap;\n\npublic final class Protocol1_20_5To1_20_3 extends BackwardsProtocol<ClientboundPacket1_20_5, ClientboundPacket1_20_3, ServerboundPacket1_20_5, ServerboundPacket1_20_3> {\n\n    public static final BackwardsMappingData MAPPINGS = new BackwardsMappingData(\"1.20.5\", \"1.20.3\", Protocol1_20_3To1_20_5.class);\n    private final EntityPacketRewriter1_20_5 entityRewriter = new EntityPacketRewriter1_20_5(this);\n    private final BlockItemPacketRewriter1_20_5 itemRewriter = new BlockItemPacketRewriter1_20_5(this);\n    private final ParticleRewriter<ClientboundPacket1_20_5> particleRewriter = new ParticleRewriter<>(this);\n    private final JsonNBTComponentRewriter<ClientboundPacket1_20_5> translatableRewriter = new ComponentRewriter1_20_5(this);\n    private final TagRewriter<ClientboundPacket1_20_5> tagRewriter = new TagRewriter<>(this);\n    private final BlockPacketRewriter1_20_5 blockRewriter = new BlockPacketRewriter1_20_5(this);\n\n    public Protocol1_20_5To1_20_3() {\n        super(ClientboundPacket1_20_5.class, ClientboundPacket1_20_3.class, ServerboundPacket1_20_5.class, ServerboundPacket1_20_3.class);\n    }\n\n    @Override\n    protected void registerPackets() {\n        super.registerPackets();\n\n        tagRewriter.addEmptyTag(RegistryType.ITEM, \"minecraft:axolotl_tempt_items\");\n        replaceClientbound(ClientboundConfigurationPackets1_20_5.UPDATE_TAGS, wrapper -> {\n            // Send off registry data first, needed for tags\n            sendRegistryData(wrapper.user());\n            tagRewriter.handleGeneric(wrapper);\n        });\n\n        registerClientbound(ClientboundConfigurationPackets1_20_5.FINISH_CONFIGURATION, wrapper -> {\n            // In case the server for some reason does not send tags\n            sendRegistryData(wrapper.user());\n        });\n        registerClientbound(ClientboundPackets1_20_5.START_CONFIGURATION, wrapper -> wrapper.user().get(RegistryDataStorage.class).clear());\n\n        registerClientbound(State.LOGIN, ClientboundLoginPackets.HELLO, wrapper -> {\n            wrapper.passthrough(Types.STRING); // Server ID\n            wrapper.passthrough(Types.BYTE_ARRAY_PRIMITIVE); // Public key\n            wrapper.passthrough(Types.BYTE_ARRAY_PRIMITIVE); // Challenge\n            wrapper.read(Types.BOOLEAN); // Authenticate\n        });\n\n        replaceClientbound(ClientboundPackets1_20_5.SERVER_DATA, wrapper -> {\n            translatableRewriter.passthroughAndProcess(wrapper); // MOTD\n            wrapper.passthrough(Types.OPTIONAL_BYTE_ARRAY_PRIMITIVE); // Icon\n            wrapper.write(Types.BOOLEAN, wrapper.user().get(SecureChatStorage.class).enforcesSecureChat());\n        });\n\n        // Always write as signed, even if there is 0 signatures attached, else the validation chain gets broken\n        registerServerbound(ServerboundPackets1_20_3.CHAT_COMMAND, ServerboundPackets1_20_5.CHAT_COMMAND_SIGNED);\n\n        registerClientbound(State.LOGIN, ClientboundLoginPackets.COOKIE_REQUEST.getId(), -1, wrapper -> handleCookieRequest(wrapper, ServerboundLoginPackets.COOKIE_RESPONSE));\n        cancelClientbound(ClientboundConfigurationPackets1_20_5.RESET_CHAT); // Old clients already reset chat when entering the configuration phase\n        registerClientbound(ClientboundConfigurationPackets1_20_5.COOKIE_REQUEST, null, wrapper -> handleCookieRequest(wrapper, ServerboundConfigurationPackets1_20_5.COOKIE_RESPONSE));\n        registerClientbound(ClientboundConfigurationPackets1_20_5.STORE_COOKIE, null, this::handleStoreCookie);\n        registerClientbound(ClientboundConfigurationPackets1_20_5.TRANSFER, null, this::handleTransfer);\n        registerClientbound(ClientboundPackets1_20_5.COOKIE_REQUEST, null, wrapper -> handleCookieRequest(wrapper, ServerboundPackets1_20_5.COOKIE_RESPONSE));\n        registerClientbound(ClientboundPackets1_20_5.STORE_COOKIE, null, this::handleStoreCookie);\n        registerClientbound(ClientboundPackets1_20_5.TRANSFER, null, this::handleTransfer);\n\n        registerClientbound(ClientboundConfigurationPackets1_20_5.SELECT_KNOWN_PACKS, null, wrapper -> {\n            wrapper.cancel();\n\n            final PacketWrapper response = wrapper.create(ServerboundConfigurationPackets1_20_5.SELECT_KNOWN_PACKS);\n            response.write(Types.VAR_INT, 0); // Empty, we don't know anything\n            response.sendToServer(Protocol1_20_5To1_20_3.class);\n        });\n\n        final CommandRewriter1_19_4<ClientboundPacket1_20_5> commandRewriter = new CommandRewriter1_19_4<>(this) {\n            @Override\n            public void handleArgument(final PacketWrapper wrapper, final String argumentType) {\n                if (argumentType.equals(\"minecraft:loot_table\")\n                    || argumentType.equals(\"minecraft:loot_predicate\")\n                    || argumentType.equals(\"minecraft:loot_modifier\")) {\n                    wrapper.write(Types.VAR_INT, 0);\n                } else {\n                    super.handleArgument(wrapper, argumentType);\n                }\n            }\n        };\n        replaceClientbound(ClientboundPackets1_20_5.COMMANDS, commandRewriter::handle1_19);\n\n        registerClientbound(State.LOGIN, ClientboundLoginPackets.LOGIN_FINISHED, wrapper -> {\n            wrapper.passthrough(Types.UUID); // UUID\n            wrapper.passthrough(Types.STRING); // Name\n            wrapper.passthrough(Types.PROFILE_PROPERTY_ARRAY);\n            wrapper.read(Types.BOOLEAN); // Strict error handling\n        });\n\n        cancelClientbound(ClientboundPackets1_20_5.PROJECTILE_POWER);\n        cancelClientbound(ClientboundPackets1_20_5.DEBUG_SAMPLE);\n    }\n\n    private void sendRegistryData(final UserConnection connection) {\n        final RegistryDataStorage registryDataStorage = connection.get(RegistryDataStorage.class);\n\n        final CompoundTag registryData = registryDataStorage.registryData();\n        if (!registryDataStorage.sentRegistryData() && !registryData.isEmpty()) {\n            final PacketWrapper registryDataPacket = PacketWrapper.create(ClientboundConfigurationPackets1_20_3.REGISTRY_DATA, connection);\n            registryDataPacket.write(Types.COMPOUND_TAG, registryData.copy());\n            registryDataPacket.send(Protocol1_20_5To1_20_3.class);\n            registryDataStorage.setSentRegistryData();\n        }\n    }\n\n    private void handleStoreCookie(final PacketWrapper wrapper) {\n        wrapper.cancel();\n\n        final String resourceLocation = wrapper.read(Types.STRING);\n        final byte[] data = wrapper.read(Types.BYTE_ARRAY_PRIMITIVE);\n        if (data.length > 5120) {\n            throw new IllegalArgumentException(\"Cookie data too large\");\n        }\n\n        wrapper.user().get(CookieStorage.class).cookies().put(resourceLocation, data);\n    }\n\n    private void handleCookieRequest(final PacketWrapper wrapper, final ServerboundPacketType responseType) {\n        wrapper.cancel();\n\n        final String resourceLocation = wrapper.read(Types.STRING);\n        final byte[] data = wrapper.user().get(CookieStorage.class).cookies().get(resourceLocation);\n        final PacketWrapper responsePacket = wrapper.create(responseType);\n        responsePacket.write(Types.STRING, resourceLocation);\n        responsePacket.write(Types.OPTIONAL_BYTE_ARRAY_PRIMITIVE, data);\n        responsePacket.sendToServer(Protocol1_20_5To1_20_3.class);\n    }\n\n    private void handleTransfer(final PacketWrapper wrapper) {\n        wrapper.cancel();\n\n        final String host = wrapper.read(Types.STRING);\n        final int port = wrapper.read(Types.VAR_INT);\n        Via.getManager().getProviders().get(TransferProvider.class).connectToServer(wrapper.user(), host, port);\n    }\n\n    @Override\n    public void init(final UserConnection user) {\n        addEntityTracker(user, new EntityTrackerBase(user, EntityTypes1_20_5.PLAYER));\n        user.put(new SecureChatStorage());\n        user.put(new CookieStorage());\n        user.put(new RegistryDataStorage());\n        user.put(new BannerPatternStorage());\n        user.put(new ArmorTrimStorage());\n    }\n\n    @Override\n    public void register(final ViaProviders providers) {\n        providers.register(TransferProvider.class, TransferProvider.NOOP);\n    }\n\n    @Override\n    public BackwardsMappingData getMappingData() {\n        return MAPPINGS;\n    }\n\n    @Override\n    public EntityPacketRewriter1_20_5 getEntityRewriter() {\n        return entityRewriter;\n    }\n\n    @Override\n    public BlockItemPacketRewriter1_20_5 getItemRewriter() {\n        return itemRewriter;\n    }\n\n    @Override\n    public BlockPacketRewriter1_20_5 getBlockRewriter() {\n        return blockRewriter;\n    }\n\n    @Override\n    public ParticleRewriter<ClientboundPacket1_20_5> getParticleRewriter() {\n        return particleRewriter;\n    }\n\n    @Override\n    public JsonNBTComponentRewriter<ClientboundPacket1_20_5> getComponentRewriter() {\n        return translatableRewriter;\n    }\n\n    @Override\n    public TagRewriter<ClientboundPacket1_20_5> getTagRewriter() {\n        return tagRewriter;\n    }\n\n    @Override\n    public VersionedTypesHolder types() {\n        return VersionedTypes.V1_20_5;\n    }\n\n    @Override\n    public VersionedTypesHolder mappedTypes() {\n        return new Types1_20_3();\n    }\n\n    @Override\n    protected PacketTypesProvider<ClientboundPacket1_20_5, ClientboundPacket1_20_3, ServerboundPacket1_20_5, ServerboundPacket1_20_3> createPacketTypesProvider() {\n        return new SimplePacketTypesProvider<>(\n            packetTypeMap(unmappedClientboundPacketType, ClientboundPackets1_20_5.class, ClientboundConfigurationPackets1_20_5.class),\n            packetTypeMap(mappedClientboundPacketType, ClientboundPackets1_20_3.class, ClientboundConfigurationPackets1_20_3.class),\n            packetTypeMap(mappedServerboundPacketType, ServerboundPackets1_20_5.class, ServerboundConfigurationPackets1_20_5.class),\n            packetTypeMap(unmappedServerboundPacketType, ServerboundPackets1_20_3.class, ServerboundConfigurationPackets1_20_2.class)\n        );\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_20_5to1_20_3/Types1_20_3.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_20_5to1_20_3;\n\nimport com.viaversion.viaversion.api.minecraft.Particle;\nimport com.viaversion.viaversion.api.minecraft.data.StructuredData;\nimport com.viaversion.viaversion.api.minecraft.data.version.VersionedStructuredDataKeys;\nimport com.viaversion.viaversion.api.minecraft.entitydata.EntityData;\nimport com.viaversion.viaversion.api.minecraft.entitydata.types.AbstractEntityDataTypes;\nimport com.viaversion.viaversion.api.minecraft.item.Item;\nimport com.viaversion.viaversion.api.type.Type;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.ArrayType;\nimport com.viaversion.viaversion.api.type.types.item.StructuredDataType;\nimport com.viaversion.viaversion.api.type.types.misc.ParticleType;\nimport com.viaversion.viaversion.api.type.types.version.VersionedTypesHolder;\nimport java.util.List;\n\nfinal class Types1_20_3 implements VersionedTypesHolder {\n    @Override\n    public Type<Item> item() {\n        return Types.ITEM1_20_2;\n    }\n\n    @Override\n    public Type<Item[]> itemArray() {\n        return Types.ITEM1_20_2_ARRAY;\n    }\n\n    @Override\n    public Type<Item> itemTemplate() {\n        return item();\n    }\n\n    @Override\n    public Type<Item> optionalItemTemplate() {\n        return item();\n    }\n\n    @Override\n    public Type<Item[]> itemTemplateArray() {\n        return itemArray();\n    }\n\n    @Override\n    public Type<Item> itemCost() {\n        return null;\n    }\n\n    @Override\n    public Type<Item> optionalItemCost() {\n        return null;\n    }\n\n    @Override\n    public Type<Item> lengthPrefixedItem() {\n        return null;\n    }\n\n    @Override\n    public StructuredDataType structuredData() {\n        return null;\n    }\n\n    @Override\n    public Type<StructuredData<?>[]> structuredDataArray() {\n        return null;\n    }\n\n    @Override\n    public VersionedStructuredDataKeys structuredDataKeys() {\n        return null;\n    }\n\n    @Override\n    public ParticleType particle() {\n        return com.viaversion.viaversion.api.type.types.version.Types1_20_3.PARTICLE;\n    }\n\n    @Override\n    public ArrayType<Particle> particles() {\n        return null;\n    }\n\n    @Override\n    public AbstractEntityDataTypes entityDataTypes() {\n        return com.viaversion.viaversion.api.type.types.version.Types1_20_3.ENTITY_DATA_TYPES;\n    }\n\n    @Override\n    public Type<List<EntityData>> entityDataList() {\n        return com.viaversion.viaversion.api.type.types.version.Types1_20_3.ENTITY_DATA_LIST;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_20_5to1_20_3/provider/NoopTransferProvider.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_20_5to1_20_3.provider;\n\nimport com.viaversion.viaversion.api.connection.UserConnection;\n\nfinal class NoopTransferProvider implements TransferProvider {\n\n    @Override\n    public void connectToServer(final UserConnection connection, final String host, final int port) {\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_20_5to1_20_3/provider/TransferProvider.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_20_5to1_20_3.provider;\n\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.platform.providers.Provider;\n\n@FunctionalInterface\npublic interface TransferProvider extends Provider {\n\n    TransferProvider NOOP = new NoopTransferProvider();\n\n    void connectToServer(UserConnection connection, String host, int port);\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_20_5to1_20_3/rewriter/BlockItemPacketRewriter1_20_5.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_20_5to1_20_3.rewriter;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.ListTag;\nimport com.viaversion.nbt.tag.Tag;\nimport com.viaversion.viabackwards.api.rewriters.BackwardsStructuredItemRewriter;\nimport com.viaversion.viabackwards.api.rewriters.StructuredEnchantmentRewriter;\nimport com.viaversion.viabackwards.protocol.v1_20_5to1_20_3.Protocol1_20_5To1_20_3;\nimport com.viaversion.viaversion.api.Via;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.data.item.ItemHasher;\nimport com.viaversion.viaversion.api.minecraft.Holder;\nimport com.viaversion.viaversion.api.minecraft.Particle;\nimport com.viaversion.viaversion.api.minecraft.SoundEvent;\nimport com.viaversion.viaversion.api.minecraft.data.StructuredDataContainer;\nimport com.viaversion.viaversion.api.minecraft.data.StructuredDataKey;\nimport com.viaversion.viaversion.api.minecraft.item.Item;\nimport com.viaversion.viaversion.api.minecraft.item.StructuredItem;\nimport com.viaversion.viaversion.api.minecraft.item.data.FireworkExplosion;\nimport com.viaversion.viaversion.api.minecraft.item.data.Fireworks;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.version.Types1_20_3;\nimport com.viaversion.viaversion.api.type.types.version.VersionedTypes;\nimport com.viaversion.viaversion.protocols.v1_20_2to1_20_3.packet.ServerboundPacket1_20_3;\nimport com.viaversion.viaversion.protocols.v1_20_2to1_20_3.packet.ServerboundPackets1_20_3;\nimport com.viaversion.viaversion.protocols.v1_20_2to1_20_3.rewriter.RecipeRewriter1_20_3;\nimport com.viaversion.viaversion.protocols.v1_20_3to1_20_5.Protocol1_20_3To1_20_5;\nimport com.viaversion.viaversion.protocols.v1_20_3to1_20_5.packet.ClientboundPacket1_20_5;\nimport com.viaversion.viaversion.protocols.v1_20_3to1_20_5.packet.ClientboundPackets1_20_5;\nimport com.viaversion.viaversion.protocols.v1_20_3to1_20_5.rewriter.StructuredDataConverter;\nimport com.viaversion.viaversion.util.Key;\nimport org.checkerframework.checker.nullness.qual.Nullable;\n\npublic final class BlockItemPacketRewriter1_20_5 extends BackwardsStructuredItemRewriter<ClientboundPacket1_20_5, ServerboundPacket1_20_3, Protocol1_20_5To1_20_3> {\n\n    private static final StructuredDataConverter DATA_CONVERTER = new StructuredDataConverter(true);\n    private final Protocol1_20_3To1_20_5 vvProtocol = Via.getManager().getProtocolManager().getProtocol(Protocol1_20_3To1_20_5.class);\n    private final StructuredEnchantmentRewriter enchantmentRewriter = new StructuredEnchantmentRewriter(this);\n\n    public BlockItemPacketRewriter1_20_5(final Protocol1_20_5To1_20_3 protocol) {\n        super(protocol);\n        enchantmentRewriter.setRewriteIds(false); // Let VV handle it\n    }\n\n    @Override\n    public void registerPackets() {\n        protocol.replaceClientbound(ClientboundPackets1_20_5.BLOCK_ENTITY_DATA, wrapper -> {\n            wrapper.passthrough(Types.BLOCK_POSITION1_14); // Position\n            wrapper.passthrough(Types.VAR_INT); // Block entity type\n            final CompoundTag tag = wrapper.passthrough(Types.TRUSTED_COMPOUND_TAG);\n            protocol.getBlockRewriter().updateBlockEntityTag(tag);\n        });\n\n        protocol.registerServerbound(ServerboundPackets1_20_3.CONTAINER_BUTTON_CLICK, wrapper -> {\n            final int containerId = wrapper.read(Types.BYTE) & 0xFF;\n            final int buttonId = wrapper.read(Types.BYTE) & 0xFF;\n            wrapper.write(Types.VAR_INT, containerId);\n            wrapper.write(Types.VAR_INT, buttonId);\n        });\n\n        protocol.replaceClientbound(ClientboundPackets1_20_5.LEVEL_PARTICLES, wrapper -> {\n            wrapper.write(Types.VAR_INT, 0); // Write dummy value, set later\n\n            wrapper.passthrough(Types.BOOLEAN); // Long Distance\n            wrapper.passthrough(Types.DOUBLE); // X\n            wrapper.passthrough(Types.DOUBLE); // Y\n            wrapper.passthrough(Types.DOUBLE); // Z\n            wrapper.passthrough(Types.FLOAT); // Offset X\n            wrapper.passthrough(Types.FLOAT); // Offset Y\n            wrapper.passthrough(Types.FLOAT); // Offset Z\n            final float data = wrapper.passthrough(Types.FLOAT);\n            wrapper.passthrough(Types.INT); // Particle Count\n\n            // Move it to the beginning, move out arguments here\n            final Particle particle = wrapper.read(VersionedTypes.V1_20_5.particle());\n            protocol.getParticleRewriter().rewriteParticle(wrapper.user(), particle);\n            if (particle.id() == protocol.getMappingData().getParticleMappings().mappedId(\"entity_effect\")) {\n                // Remove color argument\n                final int color = particle.<Integer>removeArgument(0).getValue();\n                if (data == 0) {\n                    wrapper.set(Types.FLOAT, 3, (float) color);\n                }\n            } else if (particle.id() == protocol.getMappingData().getParticleMappings().mappedId(\"dust_color_transition\")) {\n                // fromColor, toColor, scale -> fromColor, scale, toColor\n                particle.add(3, Types.FLOAT, particle.<Float>removeArgument(6).getValue());\n            }\n\n            wrapper.set(Types.VAR_INT, 0, particle.id());\n            for (final Particle.ParticleData<?> argument : particle.getArguments()) {\n                argument.write(wrapper);\n            }\n        });\n\n        protocol.replaceClientbound(ClientboundPackets1_20_5.EXPLODE, wrapper -> {\n            wrapper.passthrough(Types.DOUBLE); // X\n            wrapper.passthrough(Types.DOUBLE); // Y\n            wrapper.passthrough(Types.DOUBLE); // Z\n            wrapper.passthrough(Types.FLOAT); // Power\n            final int blocks = wrapper.passthrough(Types.VAR_INT);\n            for (int i = 0; i < blocks; i++) {\n                wrapper.passthrough(Types.BYTE); // Relative X\n                wrapper.passthrough(Types.BYTE); // Relative Y\n                wrapper.passthrough(Types.BYTE); // Relative Z\n            }\n            wrapper.passthrough(Types.FLOAT); // Knockback X\n            wrapper.passthrough(Types.FLOAT); // Knockback Y\n            wrapper.passthrough(Types.FLOAT); // Knockback Z\n            wrapper.passthrough(Types.VAR_INT); // Block interaction type\n\n            final Particle smallExplosionParticle = wrapper.passthroughAndMap(VersionedTypes.V1_20_5.particle(), Types1_20_3.PARTICLE);\n            final Particle largeExplosionParticle = wrapper.passthroughAndMap(VersionedTypes.V1_20_5.particle(), Types1_20_3.PARTICLE);\n            protocol.getParticleRewriter().rewriteParticle(wrapper.user(), smallExplosionParticle);\n            protocol.getParticleRewriter().rewriteParticle(wrapper.user(), largeExplosionParticle);\n\n            final Holder<SoundEvent> soundEventHolder = wrapper.read(Types.SOUND_EVENT);\n            if (soundEventHolder.isDirect()) {\n                final SoundEvent soundEvent = soundEventHolder.value();\n                wrapper.write(Types.STRING, soundEvent.identifier());\n                wrapper.write(Types.OPTIONAL_FLOAT, soundEvent.fixedRange());\n            } else {\n                final int soundId = protocol.getMappingData().getSoundMappings().getNewId(soundEventHolder.id());\n                final String soundKey = Protocol1_20_3To1_20_5.MAPPINGS.soundName(soundId);\n                wrapper.write(Types.STRING, soundKey != null ? soundKey : \"minecraft:entity.generic.explode\");\n                wrapper.write(Types.OPTIONAL_FLOAT, null); // Fixed range\n            }\n        });\n\n        protocol.replaceClientbound(ClientboundPackets1_20_5.MERCHANT_OFFERS, wrapper -> {\n            wrapper.passthrough(Types.VAR_INT); // Container id\n            final int size = wrapper.passthrough(Types.VAR_INT);\n            for (int i = 0; i < size; i++) {\n                final Item input = handleItemToClient(wrapper.user(), wrapper.read(VersionedTypes.V1_20_5.itemCost()));\n                cleanInput(input);\n                wrapper.write(Types.ITEM1_20_2, input);\n\n                final Item result = handleItemToClient(wrapper.user(), wrapper.read(VersionedTypes.V1_20_5.item()));\n                wrapper.write(Types.ITEM1_20_2, result);\n\n                Item secondInput = wrapper.read(VersionedTypes.V1_20_5.optionalItemCost());\n                if (secondInput != null) {\n                    secondInput = handleItemToClient(wrapper.user(), secondInput);\n                    cleanInput(secondInput);\n                }\n                wrapper.write(Types.ITEM1_20_2, secondInput);\n\n                wrapper.passthrough(Types.BOOLEAN); // Out of stock\n                wrapper.passthrough(Types.INT); // Number of trade uses\n                wrapper.passthrough(Types.INT); // Maximum number of trade uses\n                wrapper.passthrough(Types.INT); // XP\n                wrapper.passthrough(Types.INT); // Special price\n                wrapper.passthrough(Types.FLOAT); // Price multiplier\n                wrapper.passthrough(Types.INT); // Demand\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_20_5.MAP_ITEM_DATA, wrapper -> {\n            wrapper.passthrough(Types.VAR_INT); // Map id\n            wrapper.passthrough(Types.BYTE); // Scale\n            wrapper.passthrough(Types.BOOLEAN); // Locked\n            if (wrapper.passthrough(Types.BOOLEAN)) {\n                final int icons = wrapper.passthrough(Types.VAR_INT);\n                for (int i = 0; i < icons; i++) {\n                    final int decorationType = wrapper.read(Types.VAR_INT);\n                    wrapper.write(Types.VAR_INT, decorationType == 34 ? 32 : decorationType); // Trial champer to jungle temple\n                    wrapper.passthrough(Types.BYTE); // X\n                    wrapper.passthrough(Types.BYTE); // Y\n                    wrapper.passthrough(Types.BYTE); // Rotation\n                    wrapper.passthrough(Types.TRUSTED_OPTIONAL_TAG); // Display name\n                }\n            }\n        });\n\n        final RecipeRewriter1_20_3<ClientboundPacket1_20_5> recipeRewriter = new RecipeRewriter1_20_3<>(protocol);\n        protocol.registerClientbound(ClientboundPackets1_20_5.UPDATE_RECIPES, wrapper -> {\n            final int size = wrapper.passthrough(Types.VAR_INT);\n            for (int i = 0; i < size; i++) {\n                // Change order and write the type as an int\n                final String recipeIdentifier = wrapper.read(Types.STRING);\n                final int serializerTypeId = wrapper.read(Types.VAR_INT);\n                final String serializerType = protocol.getMappingData().getRecipeSerializerMappings().mappedIdentifier(serializerTypeId);\n                wrapper.write(Types.STRING, serializerType);\n                wrapper.write(Types.STRING, recipeIdentifier);\n                recipeRewriter.handleRecipeType(wrapper, Key.stripMinecraftNamespace(serializerType));\n            }\n        });\n    }\n\n    private void cleanInput(@Nullable final Item item) {\n        // Try to maybe hopefully get the tag matching to what the client will try to input by removing default data\n        if (item == null || item.tag() == null) {\n            return;\n        }\n\n        final CompoundTag tag = item.tag();\n        StructuredDataConverter.removeBackupTag(tag);\n\n        final CompoundTag display = tag.getCompoundTag(\"display\");\n        if (display != null) {\n            removeEmptyList(display, \"Lore\");\n            if (display.isEmpty()) {\n                tag.remove(\"display\");\n            }\n        }\n\n        removeEmptyList(tag, \"Enchantments\");\n        removeEmptyList(tag, \"AttributeModifiers\");\n\n        if (tag.getInt(\"RepairCost\", -1) == 0) {\n            tag.remove(\"RepairCost\");\n        }\n\n        if (tag.isEmpty()) {\n            item.setTag(null);\n        }\n    }\n\n    private void removeEmptyList(final CompoundTag tag, final String key) {\n        final ListTag<?> list = tag.getListTag(key);\n        if (list != null && list.isEmpty()) {\n            tag.remove(key);\n        }\n    }\n\n    @Override\n    public @Nullable Item handleItemToClient(final UserConnection connection, Item item) {\n        if (item.isEmpty()) {\n            // Back to null for the older protocols\n            return null;\n        }\n\n        final StructuredDataContainer data = item.dataContainer();\n        item.dataContainer().setIdLookup(protocol, true);\n        enchantmentRewriter.handleToClient(item);\n\n        item = super.handleItemToClient(connection, item);\n\n        // Text components since we skip the usual rewrite method\n        updateTextComponent(connection, item, StructuredDataKey.ITEM_NAME, \"item_name\");\n        updateTextComponent(connection, item, StructuredDataKey.CUSTOM_NAME, \"custom_name\");\n        final Tag[] lore = data.get(StructuredDataKey.LORE);\n        if (lore != null) {\n            for (final Tag tag : lore) {\n                protocol.getComponentRewriter().processTag(connection, tag);\n            }\n        }\n\n        // In 1.20.6, some items have default values which are not written into the components\n        if (item.identifier() == 1105 && !data.has(StructuredDataKey.FIREWORKS)) {\n            data.set(StructuredDataKey.FIREWORKS, new Fireworks(1, new FireworkExplosion[0]));\n        }\n\n        CompoundTag customData = data.get(StructuredDataKey.CUSTOM_DATA);\n        if (customData != null) {\n            // Copy original custom data before changes\n            customData = customData.copy();\n        }\n\n        final Item oldItem = vvProtocol.getItemRewriter().toOldItem(connection, item, DATA_CONVERTER);\n\n        if (customData != null) {\n            // We later don't know which tags are custom data and which are not because the VV conversion\n            // keeps converted data, so we backup the original custom data and restore it later\n            if (oldItem.tag() == null) {\n                oldItem.setTag(new CompoundTag());\n            }\n            oldItem.tag().put(nbtTagName(), customData);\n        } else if (oldItem.tag() != null && oldItem.tag().isEmpty()) {\n            // Improve item equality checks by removing empty tags\n            oldItem.setTag(null);\n        }\n        return oldItem;\n    }\n\n    // Items and data within components are handled in this protocol\n    @Override\n    protected void handleItemDataComponentsToClient(final UserConnection connection, final Item item, final StructuredDataContainer container) {\n    }\n\n    @Override\n    protected void handleItemDataComponentsToServer(final UserConnection connection, final Item item, final StructuredDataContainer container) {\n    }\n\n    @Override\n    protected void handleRewritablesToClient(final UserConnection connection, final StructuredDataContainer container, @Nullable final ItemHasher itemHasher) {\n    }\n\n    @Override\n    protected void handleRewritablesToServer(final UserConnection connection, final StructuredDataContainer container) {\n    }\n\n    @Override\n    public Item handleItemToServer(final UserConnection connection, @Nullable final Item item) {\n        if (item == null) {\n            // Unify as empty going forward\n            return StructuredItem.empty();\n        }\n\n        // Convert to structured item first\n        final Item structuredItem = vvProtocol.getItemRewriter().toStructuredItem(connection, item);\n\n        if (item.tag() != null && item.tag().get(nbtTagName()) instanceof final CompoundTag tag) {\n            // Set original custom data from backup\n            structuredItem.dataContainer().set(StructuredDataKey.CUSTOM_DATA, tag);\n        }\n\n        structuredItem.dataContainer().setIdLookup(protocol, false);\n        enchantmentRewriter.handleToServer(structuredItem);\n\n        return super.handleItemToServer(connection, structuredItem);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_20_5to1_20_3/rewriter/BlockPacketRewriter1_20_5.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_20_5to1_20_3.rewriter;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.IntArrayTag;\nimport com.viaversion.nbt.tag.ListTag;\nimport com.viaversion.nbt.tag.StringTag;\nimport com.viaversion.nbt.tag.Tag;\nimport com.viaversion.viabackwards.protocol.v1_20_5to1_20_3.Protocol1_20_5To1_20_3;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.blockentity.BlockEntity;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.chunk.ChunkType1_20_2;\nimport com.viaversion.viaversion.protocols.v1_20_3to1_20_5.data.BannerPatterns1_20_5;\nimport com.viaversion.viaversion.protocols.v1_20_3to1_20_5.packet.ClientboundPacket1_20_5;\nimport com.viaversion.viaversion.rewriter.BlockRewriter;\nimport com.viaversion.viaversion.util.Key;\nimport org.checkerframework.checker.nullness.qual.Nullable;\n\npublic final class BlockPacketRewriter1_20_5 extends BlockRewriter<ClientboundPacket1_20_5> {\n\n    public BlockPacketRewriter1_20_5(final Protocol1_20_5To1_20_3 protocol) {\n        super(protocol, Types.BLOCK_POSITION1_14, Types.COMPOUND_TAG, ChunkType1_20_2::new, null);\n    }\n\n    @Override\n    public void handleBlockEntity(final UserConnection connection, final BlockEntity blockEntity) {\n        updateBlockEntityTag(blockEntity.tag());\n    }\n\n    public void updateBlockEntityTag(@Nullable final CompoundTag tag) {\n        if (tag == null) {\n            return;\n        }\n\n        final Tag profileTag = tag.remove(\"profile\");\n        if (profileTag instanceof StringTag) {\n            tag.put(\"SkullOwner\", profileTag);\n        } else if (profileTag instanceof CompoundTag) {\n            updateProfileTag(tag, (CompoundTag) profileTag);\n        }\n\n        final ListTag<CompoundTag> patternsTag = tag.getListTag(\"patterns\", CompoundTag.class);\n        if (patternsTag != null) {\n            for (final CompoundTag patternTag : patternsTag) {\n                final String pattern = patternTag.getString(\"pattern\", \"\");\n                final String color = patternTag.getString(\"color\");\n                final String compactIdentifier = BannerPatterns1_20_5.fullIdToCompact(Key.stripMinecraftNamespace(pattern));\n                if (compactIdentifier == null || color == null) {\n                    continue;\n                }\n\n                patternTag.remove(\"pattern\");\n                patternTag.remove(\"color\");\n                patternTag.putString(\"Pattern\", compactIdentifier);\n                patternTag.putInt(\"Color\", colorId(color));\n            }\n\n            tag.remove(\"patterns\");\n            tag.put(\"Patterns\", patternsTag);\n        }\n    }\n\n    private void updateProfileTag(final CompoundTag tag, final CompoundTag profileTag) {\n        final CompoundTag skullOwnerTag = new CompoundTag();\n        tag.put(\"SkullOwner\", skullOwnerTag);\n\n        final String name = profileTag.getString(\"name\");\n        if (name != null) {\n            skullOwnerTag.putString(\"Name\", name);\n        }\n\n        final IntArrayTag idTag = profileTag.getIntArrayTag(\"id\");\n        if (idTag != null) {\n            skullOwnerTag.put(\"Id\", idTag);\n        }\n\n        final ListTag<CompoundTag> propertiesListTag = profileTag.getListTag(\"properties\", CompoundTag.class);\n        if (propertiesListTag == null) {\n            return;\n        }\n\n        final CompoundTag propertiesTag = new CompoundTag();\n        for (final CompoundTag propertyTag : propertiesListTag) {\n            final String property = propertyTag.getString(\"name\", \"\");\n            final String value = propertyTag.getString(\"value\", \"\");\n            final String signature = propertyTag.getString(\"signature\");\n\n            final ListTag<CompoundTag> list = new ListTag<>(CompoundTag.class);\n            final CompoundTag updatedPropertyTag = new CompoundTag();\n            updatedPropertyTag.putString(\"Value\", value);\n            if (signature != null) {\n                updatedPropertyTag.putString(\"Signature\", signature);\n            }\n            list.add(updatedPropertyTag);\n            propertiesTag.put(property, list);\n        }\n        skullOwnerTag.put(\"Properties\", propertiesTag);\n    }\n\n    private static int colorId(final String color) {\n        return switch (color) {\n            case \"orange\" -> 1;\n            case \"magenta\" -> 2;\n            case \"light_blue\" -> 3;\n            case \"yellow\" -> 4;\n            case \"lime\" -> 5;\n            case \"pink\" -> 6;\n            case \"gray\" -> 7;\n            case \"light_gray\" -> 8;\n            case \"cyan\" -> 9;\n            case \"purple\" -> 10;\n            case \"blue\" -> 11;\n            case \"brown\" -> 12;\n            case \"green\" -> 13;\n            case \"red\" -> 14;\n            case \"black\" -> 15;\n            default -> 0;\n        };\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_20_5to1_20_3/rewriter/ComponentRewriter1_20_5.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_20_5to1_20_3.rewriter;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.StringTag;\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viabackwards.api.rewriters.text.JsonNBTComponentRewriter;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.data.StructuredData;\nimport com.viaversion.viaversion.api.minecraft.data.StructuredDataContainer;\nimport com.viaversion.viaversion.api.minecraft.item.Item;\nimport com.viaversion.viaversion.api.minecraft.item.StructuredItem;\nimport com.viaversion.viaversion.api.type.types.version.VersionedTypes;\nimport com.viaversion.viaversion.protocols.v1_20_3to1_20_5.packet.ClientboundPacket1_20_5;\nimport com.viaversion.viaversion.util.ComponentUtil;\nimport com.viaversion.viaversion.util.SerializerVersion;\nimport java.util.List;\nimport org.checkerframework.checker.nullness.qual.Nullable;\n\npublic final class ComponentRewriter1_20_5 extends JsonNBTComponentRewriter<ClientboundPacket1_20_5> {\n\n    private final com.viaversion.viaversion.protocols.v1_20_3to1_20_5.rewriter.ComponentRewriter1_20_5<ClientboundPacket1_20_5> vvRewriter;\n\n    public ComponentRewriter1_20_5(final BackwardsProtocol<ClientboundPacket1_20_5, ?, ?, ?> protocol) {\n        super(protocol, ReadType.NBT);\n        vvRewriter = new com.viaversion.viaversion.protocols.v1_20_3to1_20_5.rewriter.ComponentRewriter1_20_5<>(protocol, VersionedTypes.V1_20_5.structuredData());\n    }\n\n    @Override\n    protected void handleShowItem(final UserConnection connection, final CompoundTag itemTag, @Nullable final CompoundTag componentsTag) {\n        super.handleShowItem(connection, itemTag, componentsTag);\n        if (componentsTag == null) {\n            return;\n        }\n\n        final StringTag idTag = itemTag.getStringTag(\"id\");\n        if (idTag == null) {\n            return;\n        }\n\n        final List<StructuredData<?>> data = vvRewriter.toData(connection, componentsTag);\n        if (data.isEmpty()) {\n            return;\n        }\n\n        final int identifier = this.protocol.getMappingData().getFullItemMappings().id(idTag.getValue());\n\n        final StructuredItem structuredItem = new StructuredItem(identifier, 1, new StructuredDataContainer(data.toArray(StructuredData[]::new)));\n        final Item dataItem = protocol.getItemRewriter().handleItemToClient(connection, structuredItem);\n        if (dataItem.tag() == null) {\n            return;\n        }\n\n        itemTag.remove(\"components\");\n\n        final StringTag tag = new StringTag(outputSerializerVersion().toSNBT(dataItem.tag()));\n        itemTag.put(\"tag\", ComponentUtil.trimStrings(tag));\n    }\n\n    @Override\n    protected SerializerVersion inputSerializerVersion() {\n        return SerializerVersion.V1_20_5;\n    }\n\n    @Override\n    protected SerializerVersion outputSerializerVersion() {\n        return SerializerVersion.V1_20_3;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_20_5to1_20_3/rewriter/EntityPacketRewriter1_20_5.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_20_5to1_20_3.rewriter;\n\nimport com.google.common.base.Preconditions;\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.FloatTag;\nimport com.viaversion.nbt.tag.ListTag;\nimport com.viaversion.nbt.tag.NumberTag;\nimport com.viaversion.nbt.tag.StringTag;\nimport com.viaversion.nbt.tag.Tag;\nimport com.viaversion.viabackwards.api.rewriters.EntityRewriter;\nimport com.viaversion.viabackwards.protocol.v1_20_5to1_20_3.Protocol1_20_5To1_20_3;\nimport com.viaversion.viabackwards.protocol.v1_20_5to1_20_3.storage.RegistryDataStorage;\nimport com.viaversion.viabackwards.protocol.v1_20_5to1_20_3.storage.SecureChatStorage;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.data.entity.DimensionData;\nimport com.viaversion.viaversion.api.minecraft.Particle;\nimport com.viaversion.viaversion.api.minecraft.RegistryEntry;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityType;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_20_5;\nimport com.viaversion.viaversion.api.minecraft.entitydata.EntityData;\nimport com.viaversion.viaversion.api.minecraft.item.Item;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.version.Types1_20_3;\nimport com.viaversion.viaversion.api.type.types.version.VersionedTypes;\nimport com.viaversion.viaversion.data.entity.DimensionDataImpl;\nimport com.viaversion.viaversion.protocols.v1_20_2to1_20_3.packet.ClientboundPackets1_20_3;\nimport com.viaversion.viaversion.protocols.v1_20_3to1_20_5.Protocol1_20_3To1_20_5;\nimport com.viaversion.viaversion.protocols.v1_20_3to1_20_5.data.Attributes1_20_5;\nimport com.viaversion.viaversion.protocols.v1_20_3to1_20_5.packet.ClientboundConfigurationPackets1_20_5;\nimport com.viaversion.viaversion.protocols.v1_20_3to1_20_5.packet.ClientboundPacket1_20_5;\nimport com.viaversion.viaversion.protocols.v1_20_3to1_20_5.packet.ClientboundPackets1_20_5;\nimport com.viaversion.viaversion.protocols.v1_20_3to1_20_5.storage.ArmorTrimStorage;\nimport com.viaversion.viaversion.protocols.v1_20_3to1_20_5.storage.BannerPatternStorage;\nimport com.viaversion.viaversion.util.Key;\nimport com.viaversion.viaversion.util.KeyMappings;\nimport com.viaversion.viaversion.util.MathUtil;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\npublic final class EntityPacketRewriter1_20_5 extends EntityRewriter<ClientboundPacket1_20_5, Protocol1_20_5To1_20_3> {\n\n    public EntityPacketRewriter1_20_5(final Protocol1_20_5To1_20_3 protocol) {\n        super(protocol, Types1_20_3.ENTITY_DATA_TYPES.optionalComponentType, Types1_20_3.ENTITY_DATA_TYPES.booleanType);\n    }\n\n    @Override\n    public void registerPackets() {\n        protocol.replaceClientbound(ClientboundPackets1_20_5.SET_EQUIPMENT, wrapper -> {\n            final int entityId = wrapper.passthrough(Types.VAR_INT); // Entity id\n            final EntityType type = tracker(wrapper.user()).entityType(entityId);\n            byte slot;\n            do {\n                slot = wrapper.read(Types.BYTE);\n                final Item item = protocol.getItemRewriter().handleItemToClient(wrapper.user(), wrapper.read(VersionedTypes.V1_20_5.item()));\n                final int rawSlot = slot & 0x7F;\n\n                if (rawSlot == 6) {\n                    final boolean lastSlot = (slot & 0xFFFFFF80) == 0;\n                    slot = (byte) (lastSlot ? 4 : 4 | 0xFFFFFF80); // Map body slot index to chest slot index for horses, also wolves\n\n                    if (type != null && type.isOrHasParent(EntityTypes1_20_5.LLAMA)) {\n                        // Cancel equipment and set correct entity data instead\n                        wrapper.cancel();\n                        sendCarpetColorUpdate(wrapper.user(), entityId, item);\n                    }\n                }\n\n                wrapper.write(Types.BYTE, slot);\n                wrapper.write(Types.ITEM1_20_2, item);\n            } while ((slot & 0xFFFFFF80) != 0);\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_20_5.HORSE_SCREEN_OPEN, wrapper -> {\n            wrapper.passthrough(Types.UNSIGNED_BYTE); // Container id\n\n            // The body armor slot was moved to equipment\n            final int size = wrapper.read(Types.VAR_INT);\n            wrapper.write(Types.VAR_INT, size + 1);\n        });\n\n        protocol.registerClientbound(ClientboundConfigurationPackets1_20_5.REGISTRY_DATA, wrapper -> {\n            wrapper.cancel();\n\n            final String registryKey = Key.stripMinecraftNamespace(wrapper.read(Types.STRING));\n            if (registryKey.equals(\"wolf_variant\")) {\n                // There's only one wolf variant now\n                return;\n            }\n\n            final RegistryDataStorage registryDataStorage = wrapper.user().get(RegistryDataStorage.class);\n            final RegistryEntry[] entries = wrapper.read(Types.REGISTRY_ENTRY_ARRAY);\n\n            // Track trim patterns and armor trims for conversion in items\n            if (registryKey.equals(\"banner_pattern\")) {\n                // Don't send it\n                wrapper.user().get(BannerPatternStorage.class).setBannerPatterns(toMappings(entries));\n                return;\n            }\n\n            final boolean isTrimPattern = registryKey.equals(\"trim_pattern\");\n            if (isTrimPattern) {\n                wrapper.user().get(ArmorTrimStorage.class).setTrimPatterns(toMappings(entries));\n            } else if (registryKey.equals(\"trim_material\")) {\n                wrapper.user().get(ArmorTrimStorage.class).setTrimMaterials(toMappings(entries));\n            }\n\n            // Track biome and dimension data\n            if (registryKey.equals(\"worldgen/biome\")) {\n                tracker(wrapper.user()).setBiomesSent(entries.length);\n\n                // Update format of particles\n                for (final RegistryEntry entry : entries) {\n                    if (entry.tag() == null) {\n                        continue;\n                    }\n\n                    final CompoundTag effects = ((CompoundTag) entry.tag()).getCompoundTag(\"effects\");\n                    final CompoundTag particle = effects.getCompoundTag(\"particle\");\n                    if (particle != null) {\n                        final CompoundTag particleOptions = particle.getCompoundTag(\"options\");\n                        final String particleType = particleOptions.getString(\"type\");\n                        updateParticleFormat(particleOptions, Key.stripMinecraftNamespace(particleType));\n                    }\n                }\n            } else if (registryKey.equals(\"dimension_type\")) {\n                final Map<String, DimensionData> dimensionDataMap = new HashMap<>(entries.length);\n                final String[] keys = new String[entries.length];\n                for (int i = 0; i < entries.length; i++) {\n                    final RegistryEntry entry = entries[i];\n                    Preconditions.checkNotNull(entry.tag(), \"Server unexpectedly sent null dimension data for \" + entry.key());\n\n                    final String dimensionKey = Key.stripMinecraftNamespace(entry.key());\n                    final CompoundTag tag = (CompoundTag) entry.tag();\n                    updateDimensionTypeData(tag);\n                    dimensionDataMap.put(dimensionKey, new DimensionDataImpl(i, tag));\n                    keys[i] = dimensionKey;\n                }\n                registryDataStorage.setDimensionKeys(keys);\n                tracker(wrapper.user()).setDimensions(dimensionDataMap);\n            }\n\n            // Write to old format\n            final CompoundTag registryTag = new CompoundTag();\n            final ListTag<CompoundTag> entriesTag = new ListTag<>(CompoundTag.class);\n            registryTag.putString(\"type\", registryKey);\n            registryTag.put(\"value\", entriesTag);\n            for (int i = 0; i < entries.length; i++) {\n                final RegistryEntry entry = entries[i];\n                Preconditions.checkNotNull(entry.tag(), \"Server unexpectedly sent null registry data entry for \" + entry.key());\n\n                if (isTrimPattern) {\n                    final CompoundTag patternTag = (CompoundTag) entry.tag();\n                    final StringTag templateItem = patternTag.getStringTag(\"template_item\");\n                    if (Protocol1_20_3To1_20_5.MAPPINGS.getFullItemMappings().id(templateItem.getValue()) == -1) {\n                        // Skip new items\n                        continue;\n                    }\n                }\n\n                final CompoundTag entryCompoundTag = new CompoundTag();\n                entryCompoundTag.putString(\"name\", entry.key());\n                entryCompoundTag.putInt(\"id\", i);\n                entryCompoundTag.put(\"element\", entry.tag());\n                entriesTag.add(entryCompoundTag);\n            }\n\n            // Store and send together with the rest later\n            registryDataStorage.registryData().put(registryKey, registryTag);\n        });\n\n        protocol.replaceClientbound(ClientboundPackets1_20_5.LOGIN, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.INT); // Entity id\n                map(Types.BOOLEAN); // Hardcore\n                map(Types.STRING_ARRAY); // World List\n                map(Types.VAR_INT); // Max players\n                map(Types.VAR_INT); // View distance\n                map(Types.VAR_INT); // Simulation distance\n                map(Types.BOOLEAN); // Reduced debug info\n                map(Types.BOOLEAN); // Show death screen\n                map(Types.BOOLEAN); // Limited crafting\n                handler(wrapper -> {\n                    final int dimensionId = wrapper.read(Types.VAR_INT);\n                    final RegistryDataStorage storage = wrapper.user().get(RegistryDataStorage.class);\n                    wrapper.write(Types.STRING, storage.dimensionKeys()[dimensionId]);\n                });\n                map(Types.STRING); // World\n                map(Types.LONG); // Seed\n                map(Types.BYTE); // Gamemode\n                map(Types.BYTE); // Previous gamemode\n                map(Types.BOOLEAN); // Debug\n                map(Types.BOOLEAN); // Flat\n                map(Types.OPTIONAL_GLOBAL_POSITION); // Last death location\n                map(Types.VAR_INT); // Portal cooldown\n                handler(wrapper -> {\n                    // Moved to server data\n                    final boolean enforcesSecureChat = wrapper.read(Types.BOOLEAN);\n                    wrapper.user().get(SecureChatStorage.class).setEnforcesSecureChat(enforcesSecureChat);\n                });\n                handler(worldDataTrackerHandlerByKey()); // Tracks world height and name for chunk data and entity (un)tracking\n                handler(playerTrackerHandler());\n            }\n        });\n\n        protocol.replaceClientbound(ClientboundPackets1_20_5.RESPAWN, new PacketHandlers() {\n            @Override\n            public void register() {\n                handler(wrapper -> {\n                    final int dimensionId = wrapper.read(Types.VAR_INT);\n                    final RegistryDataStorage storage = wrapper.user().get(RegistryDataStorage.class);\n                    wrapper.write(Types.STRING, storage.dimensionKeys()[dimensionId]);\n                });\n                map(Types.STRING); // World\n                handler(worldDataTrackerHandlerByKey()); // Tracks world height and name for chunk data and entity (un)tracking\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_20_5.UPDATE_MOB_EFFECT, wrapper -> {\n            wrapper.passthrough(Types.VAR_INT); // Entity ID\n            final int effectId = wrapper.passthrough(Types.VAR_INT);\n\n            final int amplifier = wrapper.read(Types.VAR_INT);\n            wrapper.write(Types.BYTE, (byte) MathUtil.clamp(amplifier, Byte.MIN_VALUE, Byte.MAX_VALUE));\n\n            wrapper.passthrough(Types.VAR_INT); // Duration\n            wrapper.passthrough(Types.BYTE); // Flags\n\n            if (effectId == 32) { // Darkness, keep a stable effect\n                final CompoundTag factorData = new CompoundTag();\n                factorData.putInt(\"padding_duration\", 22);\n                factorData.putBoolean(\"had_effect_last_tick\", true);\n                factorData.putFloat(\"factor_previous_frame\", 0);\n                factorData.putFloat(\"factor_start\", 1);\n                factorData.putFloat(\"factor_target\", 1);\n                factorData.putFloat(\"factor_current\", 1);\n                wrapper.write(Types.OPTIONAL_COMPOUND_TAG, factorData);\n            } else {\n                wrapper.write(Types.OPTIONAL_COMPOUND_TAG, null);\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_20_5.UPDATE_ATTRIBUTES, wrapper -> {\n            final int entityId = wrapper.passthrough(Types.VAR_INT);\n            final int size = wrapper.passthrough(Types.VAR_INT);\n            int newSize = size;\n            for (int i = 0; i < size; i++) {\n                // From a registry int ID to a string\n                final int attributeId = wrapper.read(Types.VAR_INT);\n                final String attribute = Attributes1_20_5.idToKey(attributeId);\n                int mappedId = protocol.getMappingData().getAttributeMappings().getNewId(attributeId);\n                if (\"generic.jump_strength\".equals(attribute)) {\n                    final EntityType type = tracker(wrapper.user()).entityType(entityId);\n                    if (type == null || !type.isOrHasParent(EntityTypes1_20_5.HORSE)) {\n                        // Jump strength only applies to horses in old versions\n                        mappedId = -1;\n                    }\n                }\n\n                if (mappedId == -1) {\n                    // Remove new attributes from the list\n                    newSize--;\n\n                    wrapper.read(Types.DOUBLE); // Base\n                    final int modifierSize = wrapper.read(Types.VAR_INT);\n                    for (int j = 0; j < modifierSize; j++) {\n                        wrapper.read(Types.UUID); // ID\n                        wrapper.read(Types.DOUBLE); // Amount\n                        wrapper.read(Types.BYTE); // Operation\n                    }\n                    continue;\n                }\n\n                wrapper.write(Types.STRING, attribute);\n\n                wrapper.passthrough(Types.DOUBLE); // Base\n                final int modifierSize = wrapper.passthrough(Types.VAR_INT);\n                for (int j = 0; j < modifierSize; j++) {\n                    wrapper.passthrough(Types.UUID); // ID\n                    wrapper.passthrough(Types.DOUBLE); // Amount\n                    wrapper.passthrough(Types.BYTE); // Operation\n                }\n            }\n\n            wrapper.set(Types.VAR_INT, 1, newSize);\n        });\n    }\n\n    private KeyMappings toMappings(final RegistryEntry[] entries) {\n        final String[] keys = new String[entries.length];\n        for (int i = 0; i < entries.length; i++) {\n            keys[i] = Key.stripMinecraftNamespace(entries[i].key());\n        }\n        return new KeyMappings(keys);\n    }\n\n    private void updateParticleFormat(final CompoundTag options, final String particleType) {\n        if (\"block\".equals(particleType) || \"block_marker\".equals(particleType) || \"falling_dust\".equals(particleType) || \"dust_pillar\".equals(particleType)) {\n            Tag blockState = options.remove(\"block_state\");\n            if (blockState instanceof StringTag) {\n                final CompoundTag compoundTag = new CompoundTag();\n                compoundTag.put(\"Name\", blockState);\n                blockState = compoundTag;\n            }\n            options.put(\"value\", blockState);\n        } else if (\"item\".equals(particleType)) {\n            Tag item = options.remove(\"item\");\n            if (item instanceof StringTag) {\n                final CompoundTag compoundTag = new CompoundTag();\n                compoundTag.put(\"id\", item);\n                item = compoundTag;\n            }\n            options.put(\"value\", item);\n        } else if (\"dust_color_transition\".equals(particleType)) {\n            moveTag(options, \"from_color\", \"fromColor\");\n            moveTag(options, \"to_color\", \"toColor\");\n        } else if (\"entity_effect\".equals(particleType)) {\n            Tag color = options.remove(\"color\");\n            if (color instanceof ListTag) {\n                //noinspection unchecked\n                ListTag<? extends NumberTag> colorParts = (ListTag<? extends NumberTag>) color;\n                color = new FloatTag(encodeARGB(\n                    colorParts.get(0).getValue().floatValue(),\n                    colorParts.get(1).getValue().floatValue(),\n                    colorParts.get(2).getValue().floatValue(),\n                    colorParts.get(3).getValue().floatValue()\n                ));\n            }\n            options.put(\"value\", color);\n        }\n    }\n\n    private int encodeARGB(final float a, final float r, final float g, final float b) {\n        final int encodedAlpha = encodeColorPart(a);\n        final int encodedRed = encodeColorPart(r);\n        final int encodedGreen = encodeColorPart(g);\n        final int encodedBlue = encodeColorPart(b);\n        return encodedAlpha << 24 | encodedRed << 16 | encodedGreen << 8 | encodedBlue;\n    }\n\n    private int encodeColorPart(final float part) {\n        return (int) Math.floor(part * 255);\n    }\n\n    private int removeAlpha(final int argb) {\n        return argb & 0x00FFFFFF;\n    }\n\n    private void moveTag(final CompoundTag compoundTag, final String from, final String to) {\n        final Tag tag = compoundTag.remove(from);\n        if (tag != null) {\n            compoundTag.put(to, tag);\n        }\n    }\n\n    private void updateDimensionTypeData(final CompoundTag elementTag) {\n        final CompoundTag monsterSpawnLightLevel = elementTag.getCompoundTag(\"monster_spawn_light_level\");\n        if (monsterSpawnLightLevel != null) {\n            final CompoundTag value = new CompoundTag();\n            monsterSpawnLightLevel.put(\"value\", value);\n            value.putInt(\"min_inclusive\", monsterSpawnLightLevel.getInt(\"min_inclusive\"));\n            value.putInt(\"max_inclusive\", monsterSpawnLightLevel.getInt(\"max_inclusive\"));\n        }\n    }\n\n    private void sendCarpetColorUpdate(final UserConnection connection, final int entityId, final Item item) {\n        final PacketWrapper setEntityData = PacketWrapper.create(ClientboundPackets1_20_3.SET_ENTITY_DATA, connection);\n        setEntityData.write(Types.VAR_INT, entityId); // Entity id\n        int color = -1;\n        if (item != null) {\n            // Convert carpet item id to dyed color id\n            if (item.identifier() >= 445 && item.identifier() <= 460) {\n                color = item.identifier() - 445;\n            }\n        }\n        final List<EntityData> entityDataList = new ArrayList<>();\n        entityDataList.add(new EntityData(20, Types1_20_3.ENTITY_DATA_TYPES.varIntType, color));\n        setEntityData.write(Types1_20_3.ENTITY_DATA_LIST, entityDataList);\n        setEntityData.send(Protocol1_20_5To1_20_3.class);\n    }\n\n    @Override\n    protected void registerRewrites() {\n        filter().handler((event, data) -> {\n            final int typeId = data.dataType().typeId();\n            if (typeId == VersionedTypes.V1_20_5.entityDataTypes.particlesType.typeId()) {\n                final Particle[] particles = data.value();\n                int color = 0;\n                for (final Particle particle : particles) {\n                    if (particle.id() == protocol.getMappingData().getParticleMappings().id(\"entity_effect\")) {\n                        // Remove color argument, use one of them for the ambient particle color\n                        color = particle.<Integer>removeArgument(0).getValue();\n                    }\n                }\n                data.setTypeAndValue(Types1_20_3.ENTITY_DATA_TYPES.varIntType, removeAlpha(color));\n                return;\n            } else if (typeId == VersionedTypes.V1_20_5.entityDataTypes.armadilloState.typeId() || typeId == VersionedTypes.V1_20_5.entityDataTypes.wolfVariantType.typeId()) {\n                event.cancel();\n                return;\n            }\n\n            int id = typeId;\n            if (typeId >= VersionedTypes.V1_20_5.entityDataTypes.armadilloState.typeId()) {\n                id--;\n            }\n            if (typeId >= VersionedTypes.V1_20_5.entityDataTypes.wolfVariantType.typeId()) {\n                id--;\n            }\n            if (typeId >= VersionedTypes.V1_20_5.entityDataTypes.particlesType.typeId()) {\n                id--;\n            }\n            data.setDataType(Types1_20_3.ENTITY_DATA_TYPES.byId(id));\n        });\n\n        registerEntityDataTypeHandler1_20_3(\n            Types1_20_3.ENTITY_DATA_TYPES.itemType,\n            Types1_20_3.ENTITY_DATA_TYPES.blockStateType,\n            Types1_20_3.ENTITY_DATA_TYPES.optionalBlockStateType,\n            Types1_20_3.ENTITY_DATA_TYPES.particleType,\n            null,\n            Types1_20_3.ENTITY_DATA_TYPES.componentType,\n            Types1_20_3.ENTITY_DATA_TYPES.optionalComponentType\n        );\n        registerBlockStateHandler(EntityTypes1_20_5.ABSTRACT_MINECART, 11);\n\n        filter().type(EntityTypes1_20_5.AREA_EFFECT_CLOUD).addIndex(9); // Color\n        filter().type(EntityTypes1_20_5.AREA_EFFECT_CLOUD).index(11).handler((event, data) -> {\n            final Particle particle = data.value();\n            if (particle.id() == protocol.getMappingData().getParticleMappings().mappedId(\"entity_effect\")) {\n                // Move color to its own entity data\n                final int color = particle.<Integer>removeArgument(0).getValue();\n                event.createExtraData(new EntityData(9, Types1_20_3.ENTITY_DATA_TYPES.varIntType, removeAlpha(color)));\n            }\n        });\n\n        filter().type(EntityTypes1_20_5.LLAMA).addIndex(20); // Carpet color\n        filter().type(EntityTypes1_20_5.ARMADILLO).removeIndex(17); // State\n        filter().type(EntityTypes1_20_5.WOLF).removeIndex(22); // Wolf variant\n        filter().type(EntityTypes1_20_5.OMINOUS_ITEM_SPAWNER).removeIndex(8); // Item\n    }\n\n    @Override\n    public void onMappingDataLoaded() {\n        super.onMappingDataLoaded();\n        mapEntityTypeWithData(EntityTypes1_20_5.ARMADILLO, EntityTypes1_20_5.COW).tagName();\n        mapEntityTypeWithData(EntityTypes1_20_5.BOGGED, EntityTypes1_20_5.STRAY).tagName();\n        mapEntityTypeWithData(EntityTypes1_20_5.BREEZE_WIND_CHARGE, EntityTypes1_20_5.WIND_CHARGE);\n        mapEntityTypeWithData(EntityTypes1_20_5.OMINOUS_ITEM_SPAWNER, EntityTypes1_20_5.TEXT_DISPLAY);\n    }\n\n    @Override\n    public EntityType typeFromId(final int type) {\n        return EntityTypes1_20_5.getTypeFromId(type);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_20_5to1_20_3/storage/CookieStorage.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_20_5to1_20_3.storage;\n\nimport com.viaversion.viaversion.api.connection.StorableObject;\nimport java.util.HashMap;\nimport java.util.Map;\n\npublic final class CookieStorage implements StorableObject {\n\n    private final Map<String, byte[]> cookies = new HashMap<>();\n\n    public Map<String, byte[]> cookies() {\n        return cookies;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_20_5to1_20_3/storage/RegistryDataStorage.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_20_5to1_20_3.storage;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.viaversion.api.connection.StorableObject;\nimport org.checkerframework.checker.nullness.qual.Nullable;\n\npublic final class RegistryDataStorage implements StorableObject {\n\n    private final CompoundTag registryData = new CompoundTag();\n    private String[] dimensionKeys;\n    private boolean sentRegistryData;\n\n    public CompoundTag registryData() {\n        return registryData;\n    }\n\n    public boolean sentRegistryData() {\n        return sentRegistryData;\n    }\n\n    public void setSentRegistryData() {\n        this.sentRegistryData = true;\n    }\n\n    public String @Nullable [] dimensionKeys() {\n        return dimensionKeys;\n    }\n\n    public void setDimensionKeys(final String[] dimensionKeys) {\n        this.dimensionKeys = dimensionKeys;\n    }\n\n    public void clear() {\n        registryData.clear();\n        dimensionKeys = null;\n        sentRegistryData = false;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_20_5to1_20_3/storage/SecureChatStorage.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_20_5to1_20_3.storage;\n\nimport com.viaversion.viaversion.api.connection.StorableObject;\n\npublic final class SecureChatStorage implements StorableObject {\n    private boolean enforcesSecureChat;\n\n    public void setEnforcesSecureChat(final boolean enforcesSecureChat) {\n        this.enforcesSecureChat = enforcesSecureChat;\n    }\n\n    public boolean enforcesSecureChat() {\n        return enforcesSecureChat;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_20to1_19_4/Protocol1_20To1_19_4.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_20to1_19_4;\n\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viabackwards.api.rewriters.text.JsonNBTComponentRewriter;\nimport com.viaversion.viabackwards.protocol.v1_20to1_19_4.data.BackwardsMappingData1_20;\nimport com.viaversion.viabackwards.protocol.v1_20to1_19_4.rewriter.BlockItemPacketRewriter1_20;\nimport com.viaversion.viabackwards.protocol.v1_20to1_19_4.rewriter.BlockPacketRewriter1_20;\nimport com.viaversion.viabackwards.protocol.v1_20to1_19_4.rewriter.EntityPacketRewriter1_20;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.RegistryType;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_19_4;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.data.entity.EntityTrackerBase;\nimport com.viaversion.viaversion.protocols.v1_19_3to1_19_4.packet.ClientboundPackets1_19_4;\nimport com.viaversion.viaversion.protocols.v1_19_3to1_19_4.packet.ServerboundPackets1_19_4;\nimport com.viaversion.viaversion.rewriter.BlockRewriter;\nimport com.viaversion.viaversion.rewriter.ParticleRewriter;\nimport com.viaversion.viaversion.rewriter.TagRewriter;\nimport com.viaversion.viaversion.rewriter.text.ComponentRewriterBase;\nimport com.viaversion.viaversion.util.ArrayUtil;\n\npublic final class Protocol1_20To1_19_4 extends BackwardsProtocol<ClientboundPackets1_19_4, ClientboundPackets1_19_4, ServerboundPackets1_19_4, ServerboundPackets1_19_4> {\n\n    public static final BackwardsMappingData1_20 MAPPINGS = new BackwardsMappingData1_20();\n    private final JsonNBTComponentRewriter<ClientboundPackets1_19_4> translatableRewriter = new JsonNBTComponentRewriter<>(this, ComponentRewriterBase.ReadType.JSON);\n    private final EntityPacketRewriter1_20 entityRewriter = new EntityPacketRewriter1_20(this);\n    private final BlockItemPacketRewriter1_20 itemRewriter = new BlockItemPacketRewriter1_20(this);\n    private final ParticleRewriter<ClientboundPackets1_19_4> particleRewriter = new ParticleRewriter<>(this);\n    private final TagRewriter<ClientboundPackets1_19_4> tagRewriter = new TagRewriter<>(this);\n    private final BlockRewriter<ClientboundPackets1_19_4> blockRewriter = new BlockPacketRewriter1_20(this);\n\n    public Protocol1_20To1_19_4() {\n        super(ClientboundPackets1_19_4.class, ClientboundPackets1_19_4.class, ServerboundPackets1_19_4.class, ServerboundPackets1_19_4.class);\n    }\n\n    @Override\n    protected void registerPackets() {\n        super.registerPackets();\n\n        tagRewriter.addEmptyTag(RegistryType.BLOCK, \"minecraft:replaceable_plants\");\n\n        registerClientbound(ClientboundPackets1_19_4.UPDATE_ENABLED_FEATURES, wrapper -> {\n            String[] enabledFeatures = wrapper.read(Types.STRING_ARRAY);\n            wrapper.write(Types.STRING_ARRAY, ArrayUtil.add(enabledFeatures, \"minecraft:update_1_20\"));\n        });\n\n        registerClientbound(ClientboundPackets1_19_4.PLAYER_COMBAT_END, wrapper -> {\n            wrapper.passthrough(Types.VAR_INT); // Duration\n            wrapper.write(Types.INT, -1); // Killer ID - unused (who knows for how long?)\n        });\n        replaceClientbound(ClientboundPackets1_19_4.PLAYER_COMBAT_KILL, wrapper -> {\n            wrapper.passthrough(Types.VAR_INT); // Player ID\n            wrapper.write(Types.INT, -1); // Killer ID - unused (who knows for how long?)\n            translatableRewriter.processText(wrapper.user(), wrapper.passthrough(Types.COMPONENT));\n        });\n    }\n\n    @Override\n    public void init(final UserConnection user) {\n        addEntityTracker(user, new EntityTrackerBase(user, EntityTypes1_19_4.PLAYER));\n    }\n\n    @Override\n    public BackwardsMappingData1_20 getMappingData() {\n        return MAPPINGS;\n    }\n\n    @Override\n    public EntityPacketRewriter1_20 getEntityRewriter() {\n        return entityRewriter;\n    }\n\n    @Override\n    public BlockItemPacketRewriter1_20 getItemRewriter() {\n        return itemRewriter;\n    }\n\n    @Override\n    public BlockRewriter<ClientboundPackets1_19_4> getBlockRewriter() {\n        return blockRewriter;\n    }\n\n    @Override\n    public ParticleRewriter<ClientboundPackets1_19_4> getParticleRewriter() {\n        return particleRewriter;\n    }\n\n    @Override\n    public JsonNBTComponentRewriter<ClientboundPackets1_19_4> getComponentRewriter() {\n        return translatableRewriter;\n    }\n\n    @Override\n    public TagRewriter<ClientboundPackets1_19_4> getTagRewriter() {\n        return tagRewriter;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_20to1_19_4/data/BackwardsMappingData1_20.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_20to1_19_4.data;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.viabackwards.api.data.BackwardsMappingData;\nimport com.viaversion.viabackwards.api.data.BackwardsMappingDataLoader;\nimport com.viaversion.viaversion.protocols.v1_19_4to1_20.Protocol1_19_4To1_20;\n\npublic class BackwardsMappingData1_20 extends BackwardsMappingData {\n\n    private CompoundTag trimPatternRegistry;\n\n    public BackwardsMappingData1_20() {\n        super(\"1.20\", \"1.19.4\", Protocol1_19_4To1_20.class);\n    }\n\n    @Override\n    protected void loadExtras(CompoundTag data) {\n        super.loadExtras(data);\n\n        trimPatternRegistry = BackwardsMappingDataLoader.INSTANCE.loadNBT(\"trim_pattern-1.19.4.nbt\");\n    }\n\n    public CompoundTag getTrimPatternRegistry() {\n        return trimPatternRegistry.copy();\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_20to1_19_4/rewriter/BlockItemPacketRewriter1_20.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_20to1_19_4.rewriter;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.StringTag;\nimport com.viaversion.nbt.tag.Tag;\nimport com.viaversion.viabackwards.api.rewriters.BackwardsItemRewriter;\nimport com.viaversion.viabackwards.protocol.v1_20to1_19_4.Protocol1_20To1_19_4;\nimport com.viaversion.viabackwards.protocol.v1_20to1_19_4.storage.BackSignEditStorage;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.BlockChangeRecord;\nimport com.viaversion.viaversion.api.minecraft.BlockPosition;\nimport com.viaversion.viaversion.api.minecraft.chunks.Chunk;\nimport com.viaversion.viaversion.api.minecraft.item.Item;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.protocols.v1_19_3to1_19_4.packet.ClientboundPackets1_19_4;\nimport com.viaversion.viaversion.protocols.v1_19_3to1_19_4.packet.ServerboundPackets1_19_4;\nimport com.viaversion.viaversion.protocols.v1_19_3to1_19_4.rewriter.RecipeRewriter1_19_4;\nimport com.viaversion.viaversion.util.Key;\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.Set;\nimport org.checkerframework.checker.nullness.qual.Nullable;\n\npublic final class BlockItemPacketRewriter1_20 extends BackwardsItemRewriter<ClientboundPackets1_19_4, ServerboundPackets1_19_4, Protocol1_20To1_19_4> {\n\n    private static final Set<String> NEW_TRIM_PATTERNS = new HashSet<>(Arrays.asList(\"host\", \"raiser\", \"shaper\", \"silence\", \"wayfinder\"));\n\n    public BlockItemPacketRewriter1_20(final Protocol1_20To1_19_4 protocol) {\n        super(protocol, Types.ITEM1_13_2, Types.ITEM1_13_2_ARRAY);\n    }\n\n    @Override\n    public void registerPackets() {\n        protocol.replaceClientbound(ClientboundPackets1_19_4.LEVEL_CHUNK_WITH_LIGHT, new PacketHandlers() {\n            @Override\n            protected void register() {\n                handler(wrapper -> {\n                    final Chunk chunk = protocol.getBlockRewriter().handleChunk1_18(wrapper);\n                    protocol.getBlockRewriter().handleBlockEntities(chunk, wrapper.user());\n                });\n                create(Types.BOOLEAN, true); // Trust edges\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_19_4.LIGHT_UPDATE, wrapper -> {\n            wrapper.passthrough(Types.VAR_INT); // X\n            wrapper.passthrough(Types.VAR_INT); // Y\n            wrapper.write(Types.BOOLEAN, true); // Trust edges\n        });\n\n        protocol.replaceClientbound(ClientboundPackets1_19_4.SECTION_BLOCKS_UPDATE, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.LONG); // Chunk position\n                create(Types.BOOLEAN, false); // Suppress light updates\n                handler(wrapper -> {\n                    for (final BlockChangeRecord record : wrapper.passthrough(Types.VAR_LONG_BLOCK_CHANGE_ARRAY)) {\n                        record.setBlockId(protocol.getMappingData().getNewBlockStateId(record.getBlockId()));\n                    }\n                });\n            }\n        });\n\n        protocol.replaceClientbound(ClientboundPackets1_19_4.UPDATE_ADVANCEMENTS, wrapper -> {\n            wrapper.passthrough(Types.BOOLEAN); // Reset/clear\n            int size = wrapper.passthrough(Types.VAR_INT); // Mapping size\n            for (int i = 0; i < size; i++) {\n                wrapper.passthrough(Types.STRING); // Identifier\n                wrapper.passthrough(Types.OPTIONAL_STRING); // Parent\n\n                // Display data\n                if (wrapper.passthrough(Types.BOOLEAN)) {\n                    wrapper.passthrough(Types.COMPONENT); // Title\n                    wrapper.passthrough(Types.COMPONENT); // Description\n                    passthroughClientboundItem(wrapper); // Icon\n                    wrapper.passthrough(Types.VAR_INT); // Frame type\n                    int flags = wrapper.passthrough(Types.INT); // Flags\n                    if ((flags & 1) != 0) {\n                        wrapper.passthrough(Types.STRING); // Background texture\n                    }\n                    wrapper.passthrough(Types.FLOAT); // X\n                    wrapper.passthrough(Types.FLOAT); // Y\n                }\n\n                wrapper.passthrough(Types.STRING_ARRAY); // Criteria\n\n                int arrayLength = wrapper.passthrough(Types.VAR_INT);\n                for (int array = 0; array < arrayLength; array++) {\n                    wrapper.passthrough(Types.STRING_ARRAY); // String array\n                }\n\n                wrapper.read(Types.BOOLEAN); // Sends telemetry\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_19_4.OPEN_SIGN_EDITOR, wrapper -> {\n            final BlockPosition position = wrapper.passthrough(Types.BLOCK_POSITION1_14);\n            final boolean frontSide = wrapper.read(Types.BOOLEAN);\n            if (frontSide) {\n                wrapper.user().remove(BackSignEditStorage.class);\n            } else {\n                wrapper.user().put(new BackSignEditStorage(position));\n            }\n        });\n        protocol.registerServerbound(ServerboundPackets1_19_4.SIGN_UPDATE, wrapper -> {\n            final BlockPosition position = wrapper.passthrough(Types.BLOCK_POSITION1_14);\n            final BackSignEditStorage backSignEditStorage = wrapper.user().remove(BackSignEditStorage.class);\n            final boolean frontSide = backSignEditStorage == null || !backSignEditStorage.position().equals(position);\n            wrapper.write(Types.BOOLEAN, frontSide);\n        });\n\n        new RecipeRewriter1_19_4<>(protocol).register(ClientboundPackets1_19_4.UPDATE_RECIPES);\n    }\n\n    @Override\n    public @Nullable Item handleItemToClient(UserConnection connection, @Nullable Item item) {\n        if (item == null) {\n            return null;\n        }\n\n        item = super.handleItemToClient(connection, item);\n\n        // Remove new trim tags\n        final CompoundTag trimTag;\n        final CompoundTag tag = item.tag();\n        if (tag != null && (trimTag = tag.getCompoundTag(\"Trim\")) != null) {\n            final StringTag patternTag = trimTag.getStringTag(\"pattern\");\n            if (patternTag != null) {\n                final String pattern = Key.stripMinecraftNamespace(patternTag.getValue());\n                if (NEW_TRIM_PATTERNS.contains(pattern)) {\n                    tag.remove(\"Trim\");\n                    tag.put(nbtTagName(\"Trim\"), trimTag);\n                }\n            }\n        }\n        return item;\n    }\n\n    @Override\n    public @Nullable Item handleItemToServer(UserConnection connection, @Nullable Item item) {\n        if (item == null) {\n            return null;\n        }\n\n        item = super.handleItemToServer(connection, item);\n\n        // Add back original trim tag\n        final Tag trimTag;\n        final CompoundTag tag = item.tag();\n        if (tag != null && (trimTag = tag.remove(nbtTagName(\"Trim\"))) != null) {\n            tag.put(\"Trim\", trimTag);\n        }\n        return item;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_20to1_19_4/rewriter/BlockPacketRewriter1_20.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_20to1_19_4.rewriter;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.ListTag;\nimport com.viaversion.nbt.tag.StringTag;\nimport com.viaversion.nbt.tag.Tag;\nimport com.viaversion.viabackwards.protocol.v1_20to1_19_4.Protocol1_20To1_19_4;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.blockentity.BlockEntity;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.chunk.ChunkType1_18;\nimport com.viaversion.viaversion.protocols.v1_19_3to1_19_4.packet.ClientboundPackets1_19_4;\nimport com.viaversion.viaversion.rewriter.BlockRewriter;\n\npublic final class BlockPacketRewriter1_20 extends BlockRewriter<ClientboundPackets1_19_4> {\n\n    public BlockPacketRewriter1_20(final Protocol1_20To1_19_4 protocol) {\n        super(protocol, Types.BLOCK_POSITION1_14, Types.NAMED_COMPOUND_TAG, ChunkType1_18::new, null);\n    }\n\n    @Override\n    public void handleBlockEntity(final UserConnection connection, final BlockEntity blockEntity) {\n        final CompoundTag tag = blockEntity.tag();\n        if (tag == null || (blockEntity.typeId() != 7 && blockEntity.typeId() != 8)) {\n            return;\n        }\n\n        final Tag frontText = tag.remove(\"front_text\");\n        tag.remove(\"back_text\");\n\n        if (frontText instanceof CompoundTag frontTextTag) {\n            writeMessages(frontTextTag, tag, false);\n            writeMessages(frontTextTag, tag, true);\n\n            final Tag color = frontTextTag.remove(\"color\");\n            if (color != null) {\n                tag.put(\"Color\", color);\n            }\n\n            final Tag glowing = frontTextTag.remove(\"has_glowing_text\");\n            if (glowing != null) {\n                tag.put(\"GlowingText\", glowing);\n            }\n        }\n    }\n\n    private void writeMessages(final CompoundTag frontText, final CompoundTag tag, final boolean filtered) {\n        final ListTag<StringTag> messages = frontText.getListTag(filtered ? \"filtered_messages\" : \"messages\", StringTag.class);\n        if (messages == null) {\n            return;\n        }\n\n        int i = 0;\n        for (final StringTag message : messages) {\n            tag.put((filtered ? \"FilteredText\" : \"Text\") + ++i, message);\n        }\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_20to1_19_4/rewriter/EntityPacketRewriter1_20.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_20to1_19_4.rewriter;\n\nimport com.google.common.collect.Sets;\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.ListTag;\nimport com.viaversion.nbt.tag.StringTag;\nimport com.viaversion.viabackwards.api.rewriters.EntityRewriter;\nimport com.viaversion.viabackwards.protocol.v1_20to1_19_4.Protocol1_20To1_19_4;\nimport com.viaversion.viaversion.api.minecraft.Quaternion;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityType;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_19_4;\nimport com.viaversion.viaversion.api.minecraft.entitydata.EntityData;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.version.Types1_19_4;\nimport com.viaversion.viaversion.api.type.types.version.Types1_20;\nimport com.viaversion.viaversion.protocols.v1_19_3to1_19_4.packet.ClientboundPackets1_19_4;\nimport com.viaversion.viaversion.util.Key;\nimport java.util.Set;\n\npublic final class EntityPacketRewriter1_20 extends EntityRewriter<ClientboundPackets1_19_4, Protocol1_20To1_19_4> {\n\n    private final Set<String> newTrimPatterns = Sets.newHashSet(\"host_armor_trim_smithing_template\", \"raiser_armor_trim_smithing_template\",\n        \"silence_armor_trim_smithing_template\", \"shaper_armor_trim_smithing_template\", \"wayfinder_armor_trim_smithing_template\");\n    private static final Quaternion Y_FLIPPED_ROTATION = new Quaternion(0, 1, 0, 0);\n\n    public EntityPacketRewriter1_20(final Protocol1_20To1_19_4 protocol) {\n        super(protocol, Types1_19_4.ENTITY_DATA_TYPES.optionalComponentType, Types1_19_4.ENTITY_DATA_TYPES.booleanType);\n    }\n\n    @Override\n    public void registerPackets() {\n        registerSetEntityData(ClientboundPackets1_19_4.SET_ENTITY_DATA, Types1_20.ENTITY_DATA_LIST, Types1_19_4.ENTITY_DATA_LIST);\n\n        protocol.registerClientbound(ClientboundPackets1_19_4.LOGIN, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.INT); // Entity id\n                map(Types.BOOLEAN); // Hardcore\n                map(Types.BYTE); // Gamemode\n                map(Types.BYTE); // Previous Gamemode\n                map(Types.STRING_ARRAY); // World List\n                map(Types.NAMED_COMPOUND_TAG); // Dimension registry\n                map(Types.STRING); // Dimension key\n                map(Types.STRING); // World\n                map(Types.LONG); // Seed\n                map(Types.VAR_INT); // Max players\n                map(Types.VAR_INT); // Chunk radius\n                map(Types.VAR_INT); // Simulation distance\n                map(Types.BOOLEAN); // Reduced debug info\n                map(Types.BOOLEAN); // Show death screen\n                map(Types.BOOLEAN); // Debug\n                map(Types.BOOLEAN); // Flat\n                map(Types.OPTIONAL_GLOBAL_POSITION); // Last death location\n                read(Types.VAR_INT); // Portal cooldown\n\n                handler(dimensionDataHandler()); // Caches dimensions to access data like height later\n                handler(biomeSizeTracker()); // Tracks the amount of biomes sent for chunk data\n                handler(worldDataTrackerHandlerByKey()); // Tracks world height and name for chunk data and entity (un)tracking\n                handler(wrapper -> {\n                    final CompoundTag registry = wrapper.get(Types.NAMED_COMPOUND_TAG, 0);\n\n                    final ListTag<CompoundTag> values;\n                    // A 1.20 server can't send this element, and the 1.20 client still works, if the element is missing\n                    // on a 1.19.4 client there is an exception, so in case the 1.20 server doesn't send the element we put in an original 1.20 element\n                    CompoundTag trimPatternTag = registry.getCompoundTag(\"minecraft:trim_pattern\");\n                    if (trimPatternTag != null || (trimPatternTag = registry.getCompoundTag(\"trim_pattern\")) != null) {\n                        values = trimPatternTag.getListTag(\"value\", CompoundTag.class);\n                    } else {\n                        final CompoundTag trimPatternRegistry = Protocol1_20To1_19_4.MAPPINGS.getTrimPatternRegistry().copy();\n                        registry.put(\"minecraft:trim_pattern\", trimPatternRegistry);\n                        values = trimPatternRegistry.getListTag(\"value\", CompoundTag.class);\n                    }\n\n                    for (final CompoundTag entry : values) {\n                        final CompoundTag element = entry.getCompoundTag(\"element\");\n                        final StringTag templateItem = element.getStringTag(\"template_item\");\n                        if (newTrimPatterns.contains(Key.stripMinecraftNamespace(templateItem.getValue()))) {\n                            templateItem.setValue(\"minecraft:spire_armor_trim_smithing_template\");\n                        }\n                    }\n                });\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_19_4.RESPAWN, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.STRING); // Dimension\n                map(Types.STRING); // World\n                map(Types.LONG); // Seed\n                map(Types.UNSIGNED_BYTE); // Gamemode\n                map(Types.BYTE); // Previous gamemode\n                map(Types.BOOLEAN); // Debug\n                map(Types.BOOLEAN); // Flat\n                map(Types.BYTE); // Data to keep\n                map(Types.OPTIONAL_GLOBAL_POSITION); // Last death location\n                read(Types.VAR_INT); // Portal cooldown\n                handler(worldDataTrackerHandlerByKey()); // Tracks world height and name for chunk data and entity (un)tracking\n            }\n        });\n    }\n\n    @Override\n    protected void registerRewrites() {\n        filter().mapDataType(Types1_19_4.ENTITY_DATA_TYPES::byId);\n        registerEntityDataTypeHandler(Types1_19_4.ENTITY_DATA_TYPES.itemType, Types1_19_4.ENTITY_DATA_TYPES.blockStateType, Types1_19_4.ENTITY_DATA_TYPES.optionalBlockStateType,\n            Types1_19_4.ENTITY_DATA_TYPES.particleType, Types1_19_4.ENTITY_DATA_TYPES.componentType, Types1_19_4.ENTITY_DATA_TYPES.optionalComponentType);\n        registerBlockStateHandler(EntityTypes1_19_4.ABSTRACT_MINECART, 11);\n\n        // Rotate item display by 180 degrees around the Y axis\n        filter().type(EntityTypes1_19_4.ITEM_DISPLAY).handler((event, data) -> {\n            if (event.trackedEntity().hasSentEntityData() || event.hasExtraData()) {\n                return;\n            }\n\n            if (event.dataAtIndex(12) == null) {\n                event.createExtraData(new EntityData(12, Types1_19_4.ENTITY_DATA_TYPES.quaternionType, Y_FLIPPED_ROTATION));\n            }\n        });\n        filter().type(EntityTypes1_19_4.ITEM_DISPLAY).index(12).handler((event, data) -> {\n            final Quaternion quaternion = data.value();\n            data.setValue(rotateY180(quaternion));\n        });\n    }\n\n    @Override\n    public EntityType typeFromId(final int type) {\n        return EntityTypes1_19_4.getTypeFromId(type);\n    }\n\n    private Quaternion rotateY180(final Quaternion quaternion) {\n        return new Quaternion(-quaternion.z(), quaternion.w(), quaternion.x(), -quaternion.y());\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_20to1_19_4/storage/BackSignEditStorage.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_20to1_19_4.storage;\n\nimport com.viaversion.viaversion.api.connection.StorableObject;\nimport com.viaversion.viaversion.api.minecraft.BlockPosition;\n\npublic record BackSignEditStorage(BlockPosition position) implements StorableObject {\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_11to1_21_9/Protocol1_21_11To1_21_9.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_11to1_21_9;\n\nimport com.viaversion.nbt.tag.ByteTag;\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.IntTag;\nimport com.viaversion.nbt.tag.ListTag;\nimport com.viaversion.nbt.tag.NumberTag;\nimport com.viaversion.nbt.tag.StringTag;\nimport com.viaversion.nbt.tag.Tag;\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viabackwards.api.data.BackwardsMappingData;\nimport com.viaversion.viabackwards.api.rewriters.BackwardsRegistryRewriter;\nimport com.viaversion.viabackwards.api.rewriters.text.NBTComponentRewriter;\nimport com.viaversion.viabackwards.protocol.v1_21_11to1_21_9.rewriter.BlockItemPacketRewriter1_21_11;\nimport com.viaversion.viabackwards.protocol.v1_21_11to1_21_9.rewriter.ComponentRewriter1_21_11;\nimport com.viaversion.viabackwards.protocol.v1_21_11to1_21_9.rewriter.EntityPacketRewriter1_21_11;\nimport com.viaversion.viabackwards.protocol.v1_21_11to1_21_9.storage.GameTimeStorage;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.data.FullMappings;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_21_11;\nimport com.viaversion.viaversion.api.protocol.packet.provider.PacketTypesProvider;\nimport com.viaversion.viaversion.api.protocol.packet.provider.SimplePacketTypesProvider;\nimport com.viaversion.viaversion.api.type.types.chunk.ChunkType1_21_5;\nimport com.viaversion.viaversion.api.type.types.version.VersionedTypes;\nimport com.viaversion.viaversion.api.type.types.version.VersionedTypesHolder;\nimport com.viaversion.viaversion.data.entity.EntityTrackerBase;\nimport com.viaversion.viaversion.data.item.ItemHasherBase;\nimport com.viaversion.viaversion.protocols.v1_21_4to1_21_5.rewriter.RecipeDisplayRewriter1_21_5;\nimport com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ServerboundPackets1_21_6;\nimport com.viaversion.viaversion.protocols.v1_21_7to1_21_9.packet.ClientboundConfigurationPackets1_21_9;\nimport com.viaversion.viaversion.protocols.v1_21_7to1_21_9.packet.ClientboundPacket1_21_9;\nimport com.viaversion.viaversion.protocols.v1_21_7to1_21_9.packet.ClientboundPackets1_21_9;\nimport com.viaversion.viaversion.protocols.v1_21_7to1_21_9.packet.ServerboundConfigurationPackets1_21_9;\nimport com.viaversion.viaversion.protocols.v1_21_7to1_21_9.packet.ServerboundPacket1_21_9;\nimport com.viaversion.viaversion.protocols.v1_21_9to1_21_11.Protocol1_21_9To1_21_11;\nimport com.viaversion.viaversion.protocols.v1_21_9to1_21_11.packet.ClientboundPacket1_21_11;\nimport com.viaversion.viaversion.protocols.v1_21_9to1_21_11.packet.ClientboundPackets1_21_11;\nimport com.viaversion.viaversion.rewriter.BlockRewriter;\nimport com.viaversion.viaversion.rewriter.ParticleRewriter;\nimport com.viaversion.viaversion.rewriter.RecipeDisplayRewriter;\nimport com.viaversion.viaversion.rewriter.TagRewriter;\nimport com.viaversion.viaversion.util.Key;\nimport com.viaversion.viaversion.util.TagUtil;\nimport java.util.function.Function;\nimport org.checkerframework.checker.nullness.qual.Nullable;\n\nimport static com.viaversion.viaversion.util.ProtocolUtil.packetTypeMap;\nimport static com.viaversion.viaversion.util.TagUtil.getNamespacedTag;\n\npublic final class Protocol1_21_11To1_21_9 extends BackwardsProtocol<ClientboundPacket1_21_11, ClientboundPacket1_21_9, ServerboundPacket1_21_9, ServerboundPacket1_21_9> {\n\n    public static final BackwardsMappingData MAPPINGS = new BackwardsMappingData(\"1.21.11\", \"1.21.9\", Protocol1_21_9To1_21_11.class);\n    private static final int END_FOG_COLOR = 10518688;\n    private static final int OVERWORLD_FOG_COLOR = -4138753;\n    private final EntityPacketRewriter1_21_11 entityRewriter = new EntityPacketRewriter1_21_11(this);\n    private final BlockItemPacketRewriter1_21_11 itemRewriter = new BlockItemPacketRewriter1_21_11(this);\n    private final ParticleRewriter<ClientboundPacket1_21_11> particleRewriter = new ParticleRewriter<>(this);\n    private final NBTComponentRewriter<ClientboundPacket1_21_11> translatableRewriter = new ComponentRewriter1_21_11(this);\n    private final TagRewriter<ClientboundPacket1_21_11> tagRewriter = new TagRewriter<>(this);\n    private final RecipeDisplayRewriter<ClientboundPacket1_21_11> recipeRewriter = new RecipeDisplayRewriter1_21_5<>(this);\n    private final BlockRewriter<ClientboundPacket1_21_11> blockRewriter = BlockRewriter.for1_20_2(this, ChunkType1_21_5::new);\n    private final BackwardsRegistryRewriter registryDataRewriter = new BackwardsRegistryRewriter(this) {\n        @Override\n        protected void updateType(final CompoundTag tag, final String key, final FullMappings mappings) {\n            super.updateType(tag, key, mappings);\n\n            if (key.equals(\"sound\") && tag.get(key) instanceof ListTag<?> listTag) {\n                // From a compact list to a single value\n                final Tag first;\n                if (listTag.isEmpty()) {\n                    first = new StringTag(mappings.mappedIdentifier(0)); // Dummy\n                } else {\n                    first = listTag.get(0);\n                }\n                tag.put(key, first);\n            }\n        }\n    };\n\n    public Protocol1_21_11To1_21_9() {\n        super(ClientboundPacket1_21_11.class, ClientboundPacket1_21_9.class, ServerboundPacket1_21_9.class, ServerboundPacket1_21_9.class);\n    }\n\n    private void moveAttribute(final CompoundTag baseTag, @Nullable final CompoundTag attributes, final String key, final String mappedKey, final Function<Tag, Tag> tagMapper, @Nullable final Tag defaultTag) {\n        final Tag attributeTag;\n        if (attributes != null && (attributeTag = getNamespacedTag(attributes, key)) != null) {\n            baseTag.put(mappedKey, tagMapper.apply(attributeTag));\n        } else if (defaultTag != null) {\n            baseTag.put(mappedKey, defaultTag);\n        }\n    }\n\n    private int floatsToARGB(final float r, final float g, final float b) {\n        return 255 << 24 | (int) (r * 255) << 16 | (int) (g * 255) << 8 | (int) (b * 255);\n    }\n\n    @Override\n    protected void registerPackets() {\n        super.registerPackets();\n\n        // Add back mandatory fields from attributes, though most don't have any use in the client\n        registryDataRewriter.addHandler(\"dimension_type\", (key, tag) -> {\n            if (Key.equals(key, \"the_nether\")) {\n                tag.putString(\"effects\", \"minecraft:the_nether\");\n                tag.putBoolean(\"natural\", false);\n            } else if (Key.equals(key, \"the_end\")) {\n                tag.putString(\"effects\", \"minecraft:the_end\");\n                tag.putBoolean(\"natural\", false);\n            } else {\n                tag.putString(\"effects\", \"minecraft:overworld\");\n                tag.putBoolean(\"natural\", true);\n            }\n\n            final ByteTag trueTag = new ByteTag((byte) 1);\n            final CompoundTag attributes = tag.getCompoundTag(\"attributes\");\n            moveAttribute(tag, attributes, \"visual/cloud_height\", \"cloud_height\", Function.identity(), null);\n            moveAttribute(tag, attributes, \"gameplay/can_start_raid\", \"has_raids\", Function.identity(), trueTag);\n            moveAttribute(tag, attributes, \"gameplay/piglins_zombify\", \"piglin_safe\", attributeTag -> ((NumberTag) attributeTag).asBoolean() ? ByteTag.ZERO : trueTag, ByteTag.ZERO);\n            moveAttribute(tag, attributes, \"gameplay/respawn_anchor_works\", \"respawn_anchor_works\", Function.identity(), trueTag);\n            moveAttribute(tag, attributes, \"gameplay/bed_rule\", \"bed_works\", attributeTag -> {\n                final CompoundTag bedRule = (CompoundTag) attributeTag;\n                return bedRule.getBoolean(\"can_sleep\") || bedRule.getBoolean(\"can_set_spawn\") ? trueTag : ByteTag.ZERO;\n            }, trueTag);\n            // Many different functions back into one, all have different effects on the client, so pick the most important one...\n            moveAttribute(tag, attributes, \"gameplay/fast_lava\", \"ultrawarm\", Function.identity(), ByteTag.ZERO);\n        });\n        registryDataRewriter.addHandler(\"worldgen/biome\", (key, tag) -> {\n            final CompoundTag effects = tag.getCompoundTag(\"effects\");\n\n            // Colors\n            moveAttribute(effects, effects, \"water_color\", \"water_color\", this::mapColor, new IntTag(4159204));\n            moveAttribute(effects, effects, \"foliage_color\", \"foliage_color\", this::mapColor, null);\n            moveAttribute(effects, effects, \"dry_foliage_color\", \"dry_foliage_color\", this::mapColor, null);\n            moveAttribute(effects, effects, \"grass_color\", \"grass_color\", this::mapColor, null);\n\n            final CompoundTag attributes = tag.removeUnchecked(\"attributes\");\n            moveAttribute(effects, attributes, \"visual/sky_color\", \"sky_color\", this::mapColor, new IntTag(0));\n            moveAttribute(effects, attributes, \"visual/water_fog_color\", \"water_fog_color\", this::mapColor, new IntTag(-16448205));\n            moveAttribute(effects, attributes, \"visual/fog_color\", \"fog_color\", this::mapColor, new IntTag(Key.equals(key, \"the_end\") ? END_FOG_COLOR : OVERWORLD_FOG_COLOR)); // overworld fog color as default\n\n            if (attributes == null) {\n                return;\n            }\n\n            // Music and sounds\n            final CompoundTag backgroundMusic = TagUtil.getNamespacedCompoundTag(attributes, \"audio/background_music\");\n            if (backgroundMusic != null) {\n                final CompoundTag def = backgroundMusic.getCompoundTag(\"default\");\n                if (def != null) {\n                    final CompoundTag data = new CompoundTag();\n                    final Tag maxDelay = def.get(\"max_delay\");\n                    final Tag minDelay = def.get(\"min_delay\");\n                    final Tag sound = def.get(\"sound\");\n                    if (maxDelay != null) {\n                        data.put(\"max_delay\", maxDelay);\n                    }\n                    if (minDelay != null) {\n                        data.put(\"min_delay\", minDelay);\n                    }\n                    if (sound != null) {\n                        data.put(\"sound\", sound);\n                    }\n\n                    // Assume this by default\n                    if (!data.contains(\"replace_current_music\")) {\n                        data.putBoolean(\"replace_current_music\", false);\n                    }\n\n                    final CompoundTag entry = new CompoundTag();\n                    entry.put(\"data\", data);\n                    entry.putInt(\"weight\", 1);\n\n                    final ListTag<CompoundTag> musicList = new ListTag<>(CompoundTag.class);\n                    musicList.add(entry);\n                    effects.put(\"music\", musicList);\n                }\n            }\n\n            final CompoundTag ambientSounds = TagUtil.getNamespacedCompoundTag(attributes, \"audio/ambient_sounds\");\n            if (ambientSounds != null) {\n                final Tag loop = ambientSounds.get(\"loop\");\n                if (loop != null) {\n                    effects.put(\"ambient_sound\", loop);\n                }\n\n                final Tag mood = ambientSounds.get(\"mood\");\n                if (mood != null) {\n                    effects.put(\"mood_sound\", mood);\n                }\n\n                final Tag additions = ambientSounds.get(\"additions\");\n                if (additions != null) {\n                    effects.put(\"additions_sound\", additions);\n                }\n            }\n\n            // Particles\n            final ListTag<CompoundTag> ambientParticles = TagUtil.getNamespacedCompoundTagList(attributes, \"visual/ambient_particles\");\n            if (ambientParticles != null && !ambientParticles.isEmpty()) {\n                final CompoundTag first = ambientParticles.get(0);\n                final Tag probability = first.get(\"probability\");\n                final CompoundTag particle = first.getCompoundTag(\"particle\");\n\n                // Single particle\n                final CompoundTag options = new CompoundTag();\n                options.put(\"probability\", probability);\n                options.put(\"options\", particle);\n                effects.put(\"particle\", options);\n            }\n        });\n        registryDataRewriter.addHandler(\"enchantment\", (key, tag) -> {\n            final CompoundTag effects = tag.getCompoundTag(\"effects\");\n            if (effects != null) {\n                TagUtil.removeNamespaced(effects, \"post_piercing_attack\");\n            }\n        });\n        registryDataRewriter.remove(\"zombie_nautilus_variant\");\n        registryDataRewriter.remove(\"timeline\");\n\n        tagRewriter.removeTags(\"timeline\");\n    }\n\n    @Override\n    public void init(final UserConnection connection) {\n        addEntityTracker(connection, new EntityTrackerBase(connection, EntityTypes1_21_11.PLAYER));\n        addItemHasher(connection, new ItemHasherBase(this, connection));\n        connection.put(new GameTimeStorage());\n    }\n\n    @Override\n    public BackwardsMappingData getMappingData() {\n        return MAPPINGS;\n    }\n\n    @Override\n    public EntityPacketRewriter1_21_11 getEntityRewriter() {\n        return entityRewriter;\n    }\n\n    @Override\n    public BlockItemPacketRewriter1_21_11 getItemRewriter() {\n        return itemRewriter;\n    }\n\n    @Override\n    public BlockRewriter<ClientboundPacket1_21_11> getBlockRewriter() {\n        return blockRewriter;\n    }\n\n    @Override\n    public BackwardsRegistryRewriter getRegistryDataRewriter() {\n        return registryDataRewriter;\n    }\n\n    @Override\n    public RecipeDisplayRewriter<ClientboundPacket1_21_11> getRecipeRewriter() {\n        return recipeRewriter;\n    }\n\n    @Override\n    public ParticleRewriter<ClientboundPacket1_21_11> getParticleRewriter() {\n        return particleRewriter;\n    }\n\n    @Override\n    public NBTComponentRewriter<ClientboundPacket1_21_11> getComponentRewriter() {\n        return translatableRewriter;\n    }\n\n    @Override\n    public TagRewriter<ClientboundPacket1_21_11> getTagRewriter() {\n        return tagRewriter;\n    }\n\n    @Override\n    public VersionedTypesHolder types() {\n        return VersionedTypes.V1_21_11;\n    }\n\n    @Override\n    public VersionedTypesHolder mappedTypes() {\n        return VersionedTypes.V1_21_9;\n    }\n\n    @Override\n    protected PacketTypesProvider<ClientboundPacket1_21_11, ClientboundPacket1_21_9, ServerboundPacket1_21_9, ServerboundPacket1_21_9> createPacketTypesProvider() {\n        return new SimplePacketTypesProvider<>(\n            packetTypeMap(unmappedClientboundPacketType, ClientboundPackets1_21_11.class, ClientboundConfigurationPackets1_21_9.class),\n            packetTypeMap(mappedClientboundPacketType, ClientboundPackets1_21_9.class, ClientboundConfigurationPackets1_21_9.class),\n            packetTypeMap(mappedServerboundPacketType, ServerboundPackets1_21_6.class, ServerboundConfigurationPackets1_21_9.class),\n            packetTypeMap(unmappedServerboundPacketType, ServerboundPackets1_21_6.class, ServerboundConfigurationPackets1_21_9.class)\n        );\n    }\n\n    private Tag mapColor(final Tag attributeTag) {\n        if (attributeTag instanceof ListTag<?> listTag) {\n            final NumberTag r = ((NumberTag) listTag.get(0));\n            final NumberTag g = ((NumberTag) listTag.get(1));\n            final NumberTag b = ((NumberTag) listTag.get(2));\n            return new IntTag(floatsToARGB(r.asFloat(), g.asFloat(), b.asFloat()));\n        } else if (attributeTag instanceof StringTag stringTag) {\n            // Remove '#' and parse hex string\n            return new IntTag(Integer.parseInt(stringTag.getValue().substring(1), 16));\n        }\n        return attributeTag;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_11to1_21_9/rewriter/BlockItemPacketRewriter1_21_11.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_11to1_21_9.rewriter;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.IntTag;\nimport com.viaversion.nbt.tag.StringTag;\nimport com.viaversion.nbt.tag.Tag;\nimport com.viaversion.viabackwards.api.rewriters.BackwardsStructuredItemRewriter;\nimport com.viaversion.viabackwards.protocol.v1_21_11to1_21_9.Protocol1_21_11To1_21_9;\nimport com.viaversion.viabackwards.protocol.v1_21_11to1_21_9.storage.GameTimeStorage;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.Holder;\nimport com.viaversion.viaversion.api.minecraft.SoundEvent;\nimport com.viaversion.viaversion.api.minecraft.data.StructuredDataContainer;\nimport com.viaversion.viaversion.api.minecraft.data.StructuredDataKey;\nimport com.viaversion.viaversion.api.minecraft.item.Item;\nimport com.viaversion.viaversion.api.minecraft.item.data.AttackRange;\nimport com.viaversion.viaversion.api.minecraft.item.data.DamageType;\nimport com.viaversion.viaversion.api.minecraft.item.data.KineticWeapon;\nimport com.viaversion.viaversion.api.minecraft.item.data.PiercingWeapon;\nimport com.viaversion.viaversion.api.minecraft.item.data.SwingAnimation;\nimport com.viaversion.viaversion.api.minecraft.item.data.UseEffects;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ServerboundPackets1_21_6;\nimport com.viaversion.viaversion.protocols.v1_21_7to1_21_9.packet.ServerboundPacket1_21_9;\nimport com.viaversion.viaversion.protocols.v1_21_9to1_21_11.packet.ClientboundPacket1_21_11;\nimport com.viaversion.viaversion.protocols.v1_21_9to1_21_11.packet.ClientboundPackets1_21_11;\nimport com.viaversion.viaversion.util.Either;\nimport org.checkerframework.checker.nullness.qual.Nullable;\n\nimport static com.viaversion.viaversion.protocols.v1_21_9to1_21_11.rewriter.BlockItemPacketRewriter1_21_11.downgradeData;\nimport static com.viaversion.viaversion.protocols.v1_21_9to1_21_11.rewriter.BlockItemPacketRewriter1_21_11.upgradeData;\n\npublic final class BlockItemPacketRewriter1_21_11 extends BackwardsStructuredItemRewriter<ClientboundPacket1_21_11, ServerboundPacket1_21_9, Protocol1_21_11To1_21_9> {\n\n    public BlockItemPacketRewriter1_21_11(final Protocol1_21_11To1_21_9 protocol) {\n        super(protocol);\n    }\n\n    @Override\n    public void registerPackets() {\n        protocol.registerClientbound(ClientboundPackets1_21_11.SET_BORDER_LERP_SIZE, wrapper -> {\n            wrapper.passthrough(Types.DOUBLE); // oldSize\n            wrapper.passthrough(Types.DOUBLE); // newSize\n            wrapper.write(Types.VAR_LONG, wrapper.read(Types.VAR_LONG) * 50); // lerpTime\n        });\n        protocol.registerClientbound(ClientboundPackets1_21_11.INITIALIZE_BORDER, wrapper -> {\n            wrapper.passthrough(Types.DOUBLE); // newCenterX\n            wrapper.passthrough(Types.DOUBLE); // newCenterZ\n            wrapper.passthrough(Types.DOUBLE); // oldSize\n            wrapper.passthrough(Types.DOUBLE); // newSize\n            wrapper.write(Types.VAR_LONG, wrapper.read(Types.VAR_LONG) * 50); // lerpTime\n        });\n        protocol.registerClientbound(ClientboundPackets1_21_11.SET_TIME, wrapper -> {\n            final long gameTime = wrapper.passthrough(Types.LONG);\n            wrapper.user().get(GameTimeStorage.class).setGameTime(gameTime);\n        });\n        protocol.registerServerbound(ServerboundPackets1_21_6.CLIENT_TICK_END, wrapper -> {\n            wrapper.user().get(GameTimeStorage.class).incrementGameTime();\n        });\n    }\n\n    @Override\n    protected void handleItemDataComponentsToClient(final UserConnection connection, final Item item, final StructuredDataContainer container) {\n        super.handleItemDataComponentsToClient(connection, item, container);\n        downgradeData(item, container);\n    }\n\n    @Override\n    protected void handleItemDataComponentsToServer(final UserConnection connection, final Item item, final StructuredDataContainer container) {\n        super.handleItemDataComponentsToServer(connection, item, container);\n        upgradeData(item, container);\n    }\n\n    @Override\n    protected void restoreBackupData(final Item item, final StructuredDataContainer container, final CompoundTag customData) {\n        super.restoreBackupData(item, container, customData);\n\n        if (!(customData.remove(nbtTagName(\"backup\")) instanceof final CompoundTag backupTag)) {\n            return;\n        }\n\n        final CompoundTag swingTag = backupTag.getCompoundTag(\"swing_animation\");\n        if (swingTag != null) {\n            final int type = swingTag.getInt(\"type\");\n            final int duration = swingTag.getInt(\"duration\");\n            container.set(StructuredDataKey.SWING_ANIMATION, new SwingAnimation(type, duration));\n        }\n\n        final CompoundTag kineticTag = backupTag.getCompoundTag(\"kinetic_weapon\");\n        if (kineticTag != null) {\n            final int contactCooldownTicks = kineticTag.getInt(\"contact_cooldown_ticks\");\n            final int delayTicks = kineticTag.getInt(\"delay_ticks\");\n            final KineticWeapon.Condition damageConditions = loadDamageCondition(kineticTag, \"damage_conditions\");\n            final KineticWeapon.Condition dismountConditions = loadDamageCondition(kineticTag, \"dismount_conditions\");\n            final KineticWeapon.Condition knockbackConditions = loadDamageCondition(kineticTag, \"knockback_conditions\");\n            final float forwardMovement = kineticTag.getFloat(\"forward_movement\");\n            final float damageMultiplier = kineticTag.getFloat(\"damage_multiplier\");\n            final Holder<SoundEvent> sound = kineticTag.contains(\"sound\") ? restoreSoundEventHolder(kineticTag, \"sound\") : null;\n            final Holder<SoundEvent> hitSound = kineticTag.contains(\"hit_sound\") ? restoreSoundEventHolder(kineticTag, \"hit_sound\") : null;\n            container.set(StructuredDataKey.KINETIC_WEAPON, new KineticWeapon(contactCooldownTicks, delayTicks, damageConditions, dismountConditions, knockbackConditions, forwardMovement, damageMultiplier, sound, hitSound));\n        }\n\n        final CompoundTag piercingTag = backupTag.getCompoundTag(\"piercing_weapon\");\n        if (piercingTag != null) {\n            final boolean dealsKnockback = piercingTag.getBoolean(\"deals_knockback\");\n            final boolean dismounts = piercingTag.getBoolean(\"dismounts\");\n            final Holder<SoundEvent> sound = piercingTag.contains(\"sound\") ? restoreSoundEventHolder(piercingTag, \"sound\") : null;\n            final Holder<SoundEvent> hitSound = piercingTag.contains(\"hit_sound\") ? restoreSoundEventHolder(piercingTag, \"hit_sound\") : null;\n            container.set(StructuredDataKey.PIERCING_WEAPON, new PiercingWeapon(dealsKnockback, dismounts, sound, hitSound));\n        }\n\n        final Tag damageTypeId = backupTag.get(\"damage_type_id\");\n        if (damageTypeId != null) {\n            if (damageTypeId instanceof StringTag stringTag) {\n                container.set(StructuredDataKey.DAMAGE_TYPE1_21_11, new DamageType(Either.right(stringTag.getValue())));\n            } else if (damageTypeId instanceof IntTag intTag) {\n                container.set(StructuredDataKey.DAMAGE_TYPE1_21_11, new DamageType(Either.left(intTag.asInt())));\n            }\n        }\n\n        final CompoundTag useEffectsTag = backupTag.getCompoundTag(\"use_effects\");\n        if (useEffectsTag != null) {\n            final boolean canSprint = useEffectsTag.getBoolean(\"can_sprint\");\n            final boolean interactVibrations = useEffectsTag.getBoolean(\"interact_vibrations\");\n            final float speedMultiplier = useEffectsTag.getFloat(\"speed_multiplier\");\n            container.set(StructuredDataKey.USE_EFFECTS, new UseEffects(canSprint, interactVibrations, speedMultiplier));\n        }\n\n        final CompoundTag attackRangeTag = backupTag.getCompoundTag(\"attack_range\");\n        if (attackRangeTag != null) {\n            final float minRange = attackRangeTag.getFloat(\"min_range\");\n            final float maxRange = attackRangeTag.getFloat(\"max_range\");\n            final float minCreativeRange = attackRangeTag.getFloat(\"min_creative_reach\");\n            final float maxCreativeRange = attackRangeTag.getFloat(\"max_creative_reach\");\n            final float hitboxMargin = attackRangeTag.getFloat(\"hitbox_margin\");\n            final float mobFactor = attackRangeTag.getFloat(\"mob_factor\");\n            container.set(StructuredDataKey.ATTACK_RANGE, new AttackRange(minRange, maxRange, minCreativeRange, maxCreativeRange, hitboxMargin, mobFactor));\n        }\n\n        final Tag zombieNautilusVariantTag = backupTag.get(\"zombie_nautilus_variant\");\n        if (zombieNautilusVariantTag != null) {\n            if (zombieNautilusVariantTag instanceof StringTag stringTag) {\n                container.set(StructuredDataKey.ZOMBIE_NAUTILUS_VARIANT1_21_11, Either.right(stringTag.getValue()));\n            } else if (zombieNautilusVariantTag instanceof IntTag intTag) {\n                container.set(StructuredDataKey.ZOMBIE_NAUTILUS_VARIANT1_21_11, Either.left(intTag.asInt()));\n            }\n        }\n\n        restoreFloatData(StructuredDataKey.MINIMUM_ATTACK_CHARGE, container, backupTag);\n    }\n\n    private KineticWeapon.Condition loadDamageCondition(final CompoundTag tag, final String key) {\n        final CompoundTag conditionTag = tag.getCompoundTag(key);\n        if (conditionTag == null) {\n            return null;\n        }\n\n        final int maxDurationTicks = conditionTag.getInt(\"max_duration_ticks\");\n        final float minSpeed = conditionTag.getFloat(\"min_speed\");\n        final float minRelativeSpeed = conditionTag.getFloat(\"min_relative_speed\");\n        return new KineticWeapon.Condition(maxDurationTicks, minSpeed, minRelativeSpeed);\n    }\n\n    @Override\n    protected void backupInconvertibleData(final UserConnection connection, final Item item, final StructuredDataContainer dataContainer, final CompoundTag backupTag) {\n        super.backupInconvertibleData(connection, item, dataContainer, backupTag);\n\n        final SwingAnimation swingAnimation = dataContainer.get(StructuredDataKey.SWING_ANIMATION);\n        if (swingAnimation != null) {\n            final CompoundTag swingTag = new CompoundTag();\n            swingTag.putInt(\"type\", swingAnimation.type());\n            swingTag.putInt(\"duration\", swingAnimation.duration());\n            backupTag.put(\"swing_animation\", swingTag);\n        }\n\n        final KineticWeapon kineticWeapon = dataContainer.get(StructuredDataKey.KINETIC_WEAPON);\n        if (kineticWeapon != null) {\n            final CompoundTag kineticTag = new CompoundTag();\n            kineticTag.putInt(\"contact_cooldown_ticks\", kineticWeapon.contactCooldownTicks());\n            kineticTag.putInt(\"delay_ticks\", kineticWeapon.delayTicks());\n            saveDamageCondition(kineticTag, \"damage_conditions\", kineticWeapon.damageConditions());\n            saveDamageCondition(kineticTag, \"dismount_conditions\", kineticWeapon.dismountConditions());\n            saveDamageCondition(kineticTag, \"knockback_conditions\", kineticWeapon.knockbackConditions());\n            kineticTag.putFloat(\"forward_movement\", kineticWeapon.forwardMovement());\n            kineticTag.putFloat(\"damage_multiplier\", kineticWeapon.damageMultiplier());\n            if (kineticWeapon.sound() != null) {\n                kineticTag.put(\"sound\", holderToTag(kineticWeapon.sound(), this::saveSoundEvent));\n            }\n            if (kineticWeapon.hitSound() != null) {\n                kineticTag.put(\"hit_sound\", holderToTag(kineticWeapon.hitSound(), this::saveSoundEvent));\n            }\n            backupTag.put(\"kinetic_weapon\", kineticTag);\n        }\n\n        final PiercingWeapon piercingWeapon = dataContainer.get(StructuredDataKey.PIERCING_WEAPON);\n        if (piercingWeapon != null) {\n            final CompoundTag piercingTag = new CompoundTag();\n            piercingTag.putBoolean(\"deals_knockback\", piercingWeapon.dealsKnockback());\n            piercingTag.putBoolean(\"dismounts\", piercingWeapon.dismounts());\n            if (piercingWeapon.sound() != null) {\n                piercingTag.put(\"sound\", holderToTag(piercingWeapon.sound(), this::saveSoundEvent));\n            }\n            if (piercingWeapon.hitSound() != null) {\n                piercingTag.put(\"hit_sound\", holderToTag(piercingWeapon.hitSound(), this::saveSoundEvent));\n            }\n            backupTag.put(\"piercing_weapon\", piercingTag);\n        }\n\n        final DamageType damageType = dataContainer.get(StructuredDataKey.DAMAGE_TYPE1_21_11);\n        if (damageType != null) {\n            if (damageType.id().isLeft()) {\n                backupTag.putInt(\"damage_type_id\", damageType.id().left());\n            } else {\n                backupTag.putString(\"damage_type_id\", damageType.id().right());\n            }\n        }\n\n        final UseEffects useEffects = dataContainer.get(StructuredDataKey.USE_EFFECTS);\n        if (useEffects != null) {\n            final CompoundTag useEffectsTag = new CompoundTag();\n            useEffectsTag.putBoolean(\"can_sprint\", useEffects.canSprint());\n            useEffectsTag.putBoolean(\"interact_vibrations\", useEffects.interactVibrations());\n            useEffectsTag.putFloat(\"speed_multiplier\", useEffects.speedMultiplier());\n            backupTag.put(\"use_effects\", useEffectsTag);\n        }\n\n        final AttackRange attackRange = dataContainer.get(StructuredDataKey.ATTACK_RANGE);\n        if (attackRange != null) {\n            final CompoundTag attackRangeTag = new CompoundTag();\n            attackRangeTag.putFloat(\"min_range\", attackRange.minRange());\n            attackRangeTag.putFloat(\"max_range\", attackRange.maxRange());\n            attackRangeTag.putFloat(\"min_creative_reach\", attackRange.minCreativeRange());\n            attackRangeTag.putFloat(\"max_creative_reach\", attackRange.maxCreativeRange());\n            attackRangeTag.putFloat(\"hitbox_margin\", attackRange.hitboxMargin());\n            attackRangeTag.putFloat(\"mob_factor\", attackRange.mobFactor());\n            backupTag.put(\"attack_range\", attackRangeTag);\n        }\n\n        final Either<Integer, String> zombieNautilusVariant = dataContainer.get(StructuredDataKey.ZOMBIE_NAUTILUS_VARIANT1_21_11);\n        if (zombieNautilusVariant != null) {\n            if (zombieNautilusVariant.isLeft()) {\n                backupTag.putInt(\"zombie_nautilus_variant\", zombieNautilusVariant.left());\n            } else {\n                backupTag.putString(\"zombie_nautilus_variant\", zombieNautilusVariant.right());\n            }\n        }\n\n        saveFloatData(StructuredDataKey.MINIMUM_ATTACK_CHARGE, dataContainer, backupTag);\n    }\n\n    private void saveDamageCondition(final CompoundTag tag, final String key, final KineticWeapon.@Nullable Condition condition) {\n        if (condition == null) {\n            return;\n        }\n\n        final CompoundTag conditionTag = new CompoundTag();\n        conditionTag.putInt(\"max_duration_ticks\", condition.maxDurationTicks());\n        conditionTag.putFloat(\"min_speed\", condition.minSpeed());\n        conditionTag.putFloat(\"min_relative_speed\", condition.minRelativeSpeed());\n        tag.put(key, conditionTag);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_11to1_21_9/rewriter/ComponentRewriter1_21_11.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_11to1_21_9.rewriter;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viabackwards.api.rewriters.text.NBTComponentRewriter;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.data.StructuredDataKey;\nimport com.viaversion.viaversion.protocols.v1_21_9to1_21_11.packet.ClientboundPacket1_21_11;\n\npublic final class ComponentRewriter1_21_11 extends NBTComponentRewriter<ClientboundPacket1_21_11> {\n\n    public ComponentRewriter1_21_11(final BackwardsProtocol<ClientboundPacket1_21_11, ?, ?, ?> protocol) {\n        super(protocol);\n    }\n\n    @Override\n    protected void handleShowItem(final UserConnection connection, final CompoundTag itemTag, final CompoundTag componentsTag) {\n        super.handleShowItem(connection, itemTag, componentsTag);\n        if (componentsTag == null) {\n            return;\n        }\n\n        removeDataComponents(componentsTag, StructuredDataKey.SWING_ANIMATION, StructuredDataKey.KINETIC_WEAPON, StructuredDataKey.PIERCING_WEAPON,\n            StructuredDataKey.DAMAGE_TYPE26_1, StructuredDataKey.MINIMUM_ATTACK_CHARGE, StructuredDataKey.USE_EFFECTS, StructuredDataKey.ZOMBIE_NAUTILUS_VARIANT1_21_11, StructuredDataKey.ATTACK_RANGE);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_11to1_21_9/rewriter/EntityPacketRewriter1_21_11.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_11to1_21_9.rewriter;\n\nimport com.viaversion.viabackwards.api.rewriters.EntityRewriter;\nimport com.viaversion.viabackwards.protocol.v1_21_11to1_21_9.Protocol1_21_11To1_21_9;\nimport com.viaversion.viabackwards.protocol.v1_21_11to1_21_9.storage.GameTimeStorage;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityType;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_21_11;\nimport com.viaversion.viaversion.api.minecraft.entitydata.EntityData;\nimport com.viaversion.viaversion.api.minecraft.entitydata.types.EntityDataTypes1_21_11;\nimport com.viaversion.viaversion.api.minecraft.entitydata.types.EntityDataTypes1_21_9;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.version.VersionedTypes;\nimport com.viaversion.viaversion.protocols.v1_21_7to1_21_9.packet.ClientboundPackets1_21_9;\nimport com.viaversion.viaversion.protocols.v1_21_9to1_21_11.packet.ClientboundPacket1_21_11;\nimport com.viaversion.viaversion.protocols.v1_21_9to1_21_11.packet.ClientboundPackets1_21_11;\nimport com.viaversion.viaversion.rewriter.entitydata.EntityDataHandlerEvent;\nimport com.viaversion.viaversion.util.MathUtil;\n\npublic final class EntityPacketRewriter1_21_11 extends EntityRewriter<ClientboundPacket1_21_11, Protocol1_21_11To1_21_9> {\n\n    public EntityPacketRewriter1_21_11(final Protocol1_21_11To1_21_9 protocol) {\n        super(protocol, VersionedTypes.V1_21_9.entityDataTypes.optionalComponentType, VersionedTypes.V1_21_9.entityDataTypes.booleanType);\n    }\n\n    @Override\n    public void registerPackets() {\n        protocol.registerClientbound(ClientboundPackets1_21_11.MOUNT_SCREEN_OPEN, ClientboundPackets1_21_9.HORSE_SCREEN_OPEN);\n\n        protocol.registerClientbound(ClientboundPackets1_21_11.UPDATE_MOB_EFFECT, wrapper -> {\n            wrapper.passthrough(Types.VAR_INT); // entity id\n            final int effectId = wrapper.passthrough(Types.VAR_INT);\n            if (effectId == 39) { // breath_of_the_nautilus\n                wrapper.cancel();\n            }\n        });\n        protocol.registerClientbound(ClientboundPackets1_21_11.REMOVE_MOB_EFFECT, wrapper -> {\n            wrapper.passthrough(Types.VAR_INT); // entity id\n            final int effectId = wrapper.passthrough(Types.VAR_INT);\n            if (effectId == 39) { // breath_of_the_nautilus\n                wrapper.cancel();\n            }\n        });\n    }\n\n    @Override\n    protected void registerRewrites() {\n        final EntityDataTypes1_21_11 unmappedDataTypes = VersionedTypes.V1_21_11.entityDataTypes;\n        final EntityDataTypes1_21_9 entityDataTypes = VersionedTypes.V1_21_9.entityDataTypes;\n        dataTypeMapper()\n            .removed(unmappedDataTypes.zombieNautilusVariantType)\n            .skip(unmappedDataTypes.humanoidArmType)\n            .register();\n        filter().dataType(unmappedDataTypes.humanoidArmType).handler((event, data) -> {\n            final int arm = data.value();\n            data.setTypeAndValue(entityDataTypes.byteType, (byte) arm);\n        });\n\n        registerEntityDataTypeHandler1_20_3(\n            entityDataTypes.itemType,\n            entityDataTypes.blockStateType,\n            entityDataTypes.optionalBlockStateType,\n            entityDataTypes.particleType,\n            entityDataTypes.particlesType,\n            entityDataTypes.componentType,\n            entityDataTypes.optionalComponentType\n        );\n\n        filter().type(EntityTypes1_21_11.WOLF).index(21).handler(this::absoluteToRelativeTicks);\n        filter().type(EntityTypes1_21_11.BEE).index(18).handler(this::absoluteToRelativeTicks);\n\n        filter().type(EntityTypes1_21_11.ABSTRACT_NAUTILUS).cancel(17); // Tamable flags\n        filter().type(EntityTypes1_21_11.ABSTRACT_NAUTILUS).cancel(18); // Owner UUID\n        filter().type(EntityTypes1_21_11.ABSTRACT_NAUTILUS).cancel(19); // Dashing\n        filter().type(EntityTypes1_21_11.ZOMBIE_NAUTILUS).cancel(20); // Variant\n    }\n\n    private void absoluteToRelativeTicks(final EntityDataHandlerEvent event, final EntityData data) {\n        final long currentGameTime = event.user().get(GameTimeStorage.class).gameTime();\n        final long angerEndTime = data.value();\n        final int angerEndIn = (int) MathUtil.clamp(angerEndTime - currentGameTime, Integer.MIN_VALUE, Integer.MAX_VALUE);\n        data.setTypeAndValue(VersionedTypes.V1_21_9.entityDataTypes.varIntType, Math.max(angerEndIn, 0));\n    }\n\n    @Override\n    public void onMappingDataLoaded() {\n        super.onMappingDataLoaded();\n        mapEntityTypeWithData(EntityTypes1_21_11.NAUTILUS, EntityTypes1_21_11.SQUID).tagName();\n        mapEntityTypeWithData(EntityTypes1_21_11.ZOMBIE_NAUTILUS, EntityTypes1_21_11.GLOW_SQUID).tagName();\n        mapEntityTypeWithData(EntityTypes1_21_11.CAMEL_HUSK, EntityTypes1_21_11.CAMEL).tagName();\n        mapEntityTypeWithData(EntityTypes1_21_11.PARCHED, EntityTypes1_21_11.SKELETON).tagName();\n    }\n\n    @Override\n    public EntityType typeFromId(final int type) {\n        return EntityTypes1_21_11.getTypeFromId(type);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_11to1_21_9/storage/GameTimeStorage.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_11to1_21_9.storage;\n\nimport com.viaversion.viaversion.api.connection.StorableObject;\n\npublic final class GameTimeStorage implements StorableObject {\n\n    private long gameTime;\n\n    public long gameTime() {\n        return gameTime;\n    }\n\n    public void setGameTime(final long gameTime) {\n        this.gameTime = gameTime;\n    }\n\n    public void incrementGameTime() {\n        gameTime++;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_2to1_21/Protocol1_21_2To1_21.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_2to1_21;\n\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viabackwards.api.data.BackwardsMappingData;\nimport com.viaversion.viabackwards.api.rewriters.BackwardsRegistryRewriter;\nimport com.viaversion.viabackwards.api.rewriters.text.JsonNBTComponentRewriter;\nimport com.viaversion.viabackwards.protocol.v1_21_2to1_21.rewriter.BlockItemPacketRewriter1_21_2;\nimport com.viaversion.viabackwards.protocol.v1_21_2to1_21.rewriter.ComponentRewriter1_21_2;\nimport com.viaversion.viabackwards.protocol.v1_21_2to1_21.rewriter.EntityPacketRewriter1_21_2;\nimport com.viaversion.viabackwards.protocol.v1_21_2to1_21.rewriter.ParticleRewriter1_21_2;\nimport com.viaversion.viabackwards.protocol.v1_21_2to1_21.storage.InventoryStateIdStorage;\nimport com.viaversion.viabackwards.protocol.v1_21_2to1_21.storage.ItemTagStorage;\nimport com.viaversion.viabackwards.protocol.v1_21_2to1_21.storage.PlayerStorage;\nimport com.viaversion.viabackwards.protocol.v1_21_2to1_21.storage.RecipeStorage;\nimport com.viaversion.viabackwards.protocol.v1_21_2to1_21.storage.SignStorage;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_20_5;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.protocol.packet.State;\nimport com.viaversion.viaversion.api.protocol.packet.provider.PacketTypesProvider;\nimport com.viaversion.viaversion.api.protocol.packet.provider.SimplePacketTypesProvider;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.chunk.ChunkType1_20_2;\nimport com.viaversion.viaversion.api.type.types.version.VersionedTypes;\nimport com.viaversion.viaversion.api.type.types.version.VersionedTypesHolder;\nimport com.viaversion.viaversion.data.entity.EntityTrackerBase;\nimport com.viaversion.viaversion.protocols.base.ClientboundLoginPackets;\nimport com.viaversion.viaversion.protocols.v1_20_3to1_20_5.packet.ServerboundConfigurationPackets1_20_5;\nimport com.viaversion.viaversion.protocols.v1_20_3to1_20_5.packet.ServerboundPacket1_20_5;\nimport com.viaversion.viaversion.protocols.v1_20_3to1_20_5.packet.ServerboundPackets1_20_5;\nimport com.viaversion.viaversion.protocols.v1_20_5to1_21.packet.ClientboundConfigurationPackets1_21;\nimport com.viaversion.viaversion.protocols.v1_20_5to1_21.packet.ClientboundPacket1_21;\nimport com.viaversion.viaversion.protocols.v1_20_5to1_21.packet.ClientboundPackets1_21;\nimport com.viaversion.viaversion.protocols.v1_21to1_21_2.Protocol1_21To1_21_2;\nimport com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ClientboundPacket1_21_2;\nimport com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ClientboundPackets1_21_2;\nimport com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ServerboundPacket1_21_2;\nimport com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ServerboundPackets1_21_2;\nimport com.viaversion.viaversion.rewriter.BlockRewriter;\nimport com.viaversion.viaversion.rewriter.TagRewriter;\nimport com.viaversion.viaversion.util.ArrayUtil;\n\nimport static com.viaversion.viaversion.util.ProtocolUtil.packetTypeMap;\n\npublic final class Protocol1_21_2To1_21 extends BackwardsProtocol<ClientboundPacket1_21_2, ClientboundPacket1_21, ServerboundPacket1_21_2, ServerboundPacket1_20_5> {\n\n    public static final BackwardsMappingData MAPPINGS = new BackwardsMappingData(\"1.21.2\", \"1.21\", Protocol1_21To1_21_2.class);\n    private final EntityPacketRewriter1_21_2 entityRewriter = new EntityPacketRewriter1_21_2(this);\n    private final BlockItemPacketRewriter1_21_2 itemRewriter = new BlockItemPacketRewriter1_21_2(this);\n    private final ParticleRewriter1_21_2 particleRewriter = new ParticleRewriter1_21_2(this);\n    private final JsonNBTComponentRewriter<ClientboundPacket1_21_2> translatableRewriter = new ComponentRewriter1_21_2(this);\n    private final TagRewriter<ClientboundPacket1_21_2> tagRewriter = new TagRewriter<>(this);\n    private final BackwardsRegistryRewriter registryDataRewriter = new BackwardsRegistryRewriter(this);\n    private final BlockRewriter<ClientboundPacket1_21_2> blockRewriter = BlockRewriter.for1_20_2(this, ChunkType1_20_2::new);\n\n    public Protocol1_21_2To1_21() {\n        super(ClientboundPacket1_21_2.class, ClientboundPacket1_21.class, ServerboundPacket1_21_2.class, ServerboundPacket1_20_5.class);\n    }\n\n    @Override\n    protected void registerPackets() {\n        super.registerPackets();\n\n        replaceClientbound(ClientboundPackets1_21_2.UPDATE_TAGS, this::storeTags);\n        replaceClientbound(ClientboundConfigurationPackets1_21.UPDATE_TAGS, this::storeTags);\n\n        registerServerbound(ServerboundPackets1_20_5.CLIENT_INFORMATION, this::clientInformation);\n        registerServerbound(ServerboundConfigurationPackets1_20_5.CLIENT_INFORMATION, this::clientInformation);\n\n        registerClientbound(ClientboundConfigurationPackets1_21.UPDATE_ENABLED_FEATURES, wrapper -> {\n            final String[] enabledFeatures = wrapper.read(Types.STRING_ARRAY);\n            wrapper.write(Types.STRING_ARRAY, ArrayUtil.add(enabledFeatures, \"bundle\"));\n        });\n\n        cancelClientbound(ClientboundPackets1_21_2.MOVE_MINECART_ALONG_TRACK); // TODO\n\n        registerClientbound(State.LOGIN, ClientboundLoginPackets.LOGIN_FINISHED, wrapper -> {\n            wrapper.passthrough(Types.UUID); // UUID\n            wrapper.passthrough(Types.STRING); // Name\n            wrapper.passthrough(Types.PROFILE_PROPERTY_ARRAY);\n            wrapper.write(Types.BOOLEAN, true); // Strict error handling. Always enabled for newer clients, so mimic that behavior\n        });\n\n        registerClientbound(ClientboundPackets1_21_2.SET_TIME, wrapper -> {\n            wrapper.passthrough(Types.LONG); // Game time\n\n            long dayTime = wrapper.read(Types.LONG);\n            final boolean daylightCycle = wrapper.read(Types.BOOLEAN);\n            if (!daylightCycle) {\n                if (dayTime == 0) {\n                    dayTime = -1;\n                } else {\n                    dayTime = -dayTime;\n                }\n            }\n            wrapper.write(Types.LONG, dayTime);\n        });\n    }\n\n    private void storeTags(final PacketWrapper wrapper) {\n        tagRewriter.handleGeneric(wrapper);\n        wrapper.resetReader();\n        wrapper.user().get(ItemTagStorage.class).readItemTags(wrapper);\n    }\n\n    private void clientInformation(final PacketWrapper wrapper) {\n        wrapper.passthrough(Types.STRING); // Locale\n        wrapper.passthrough(Types.BYTE); // View distance\n        wrapper.passthrough(Types.VAR_INT); // Chat visibility\n        wrapper.passthrough(Types.BOOLEAN); // Chat colors\n        wrapper.passthrough(Types.UNSIGNED_BYTE); // Skin parts\n        wrapper.passthrough(Types.VAR_INT); // Main hand\n        wrapper.passthrough(Types.BOOLEAN); // Text filtering enabled\n        wrapper.passthrough(Types.BOOLEAN); // Allow listing\n        wrapper.write(Types.VAR_INT, 0); // Particle status, assume 'all'\n    }\n\n    @Override\n    public void init(final UserConnection user) {\n        addEntityTracker(user, new EntityTrackerBase(user, EntityTypes1_20_5.PLAYER));\n        user.put(new InventoryStateIdStorage());\n        user.put(new ItemTagStorage());\n        user.put(new RecipeStorage(this));\n        user.put(new PlayerStorage());\n        user.put(new SignStorage());\n    }\n\n    @Override\n    public BackwardsMappingData getMappingData() {\n        return MAPPINGS;\n    }\n\n    @Override\n    public EntityPacketRewriter1_21_2 getEntityRewriter() {\n        return entityRewriter;\n    }\n\n    @Override\n    public BlockItemPacketRewriter1_21_2 getItemRewriter() {\n        return itemRewriter;\n    }\n\n    @Override\n    public BlockRewriter<ClientboundPacket1_21_2> getBlockRewriter() {\n        return blockRewriter;\n    }\n\n    @Override\n    public ParticleRewriter1_21_2 getParticleRewriter() {\n        return particleRewriter;\n    }\n\n    @Override\n    public BackwardsRegistryRewriter getRegistryDataRewriter() {\n        return registryDataRewriter;\n    }\n\n    @Override\n    public JsonNBTComponentRewriter<ClientboundPacket1_21_2> getComponentRewriter() {\n        return translatableRewriter;\n    }\n\n    @Override\n    public TagRewriter<ClientboundPacket1_21_2> getTagRewriter() {\n        return tagRewriter;\n    }\n\n    @Override\n    public VersionedTypesHolder types() {\n        return VersionedTypes.V1_21_2;\n    }\n\n    @Override\n    public VersionedTypesHolder mappedTypes() {\n        return VersionedTypes.V1_21;\n    }\n\n    @Override\n    protected PacketTypesProvider<ClientboundPacket1_21_2, ClientboundPacket1_21, ServerboundPacket1_21_2, ServerboundPacket1_20_5> createPacketTypesProvider() {\n        return new SimplePacketTypesProvider<>(\n            packetTypeMap(unmappedClientboundPacketType, ClientboundPackets1_21_2.class, ClientboundConfigurationPackets1_21.class),\n            packetTypeMap(mappedClientboundPacketType, ClientboundPackets1_21.class, ClientboundConfigurationPackets1_21.class),\n            packetTypeMap(mappedServerboundPacketType, ServerboundPackets1_21_2.class, ServerboundConfigurationPackets1_20_5.class),\n            packetTypeMap(unmappedServerboundPacketType, ServerboundPackets1_20_5.class, ServerboundConfigurationPackets1_20_5.class)\n        );\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_2to1_21/rewriter/BlockItemPacketRewriter1_21_2.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_2to1_21.rewriter;\n\nimport com.viaversion.nbt.tag.ByteTag;\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.IntTag;\nimport com.viaversion.nbt.tag.ListTag;\nimport com.viaversion.nbt.tag.Tag;\nimport com.viaversion.viabackwards.api.rewriters.BackwardsStructuredItemRewriter;\nimport com.viaversion.viabackwards.protocol.v1_21_2to1_21.Protocol1_21_2To1_21;\nimport com.viaversion.viabackwards.protocol.v1_21_2to1_21.storage.InventoryStateIdStorage;\nimport com.viaversion.viabackwards.protocol.v1_21_2to1_21.storage.RecipeStorage;\nimport com.viaversion.viabackwards.protocol.v1_21_2to1_21.storage.SignStorage;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.data.MappingData;\nimport com.viaversion.viaversion.api.data.entity.EntityTracker;\nimport com.viaversion.viaversion.api.minecraft.BlockChangeRecord;\nimport com.viaversion.viaversion.api.minecraft.BlockPosition;\nimport com.viaversion.viaversion.api.minecraft.Holder;\nimport com.viaversion.viaversion.api.minecraft.HolderSet;\nimport com.viaversion.viaversion.api.minecraft.Particle;\nimport com.viaversion.viaversion.api.minecraft.SoundEvent;\nimport com.viaversion.viaversion.api.minecraft.chunks.Chunk;\nimport com.viaversion.viaversion.api.minecraft.chunks.ChunkSection;\nimport com.viaversion.viaversion.api.minecraft.chunks.DataPalette;\nimport com.viaversion.viaversion.api.minecraft.chunks.PaletteType;\nimport com.viaversion.viaversion.api.minecraft.data.StructuredDataContainer;\nimport com.viaversion.viaversion.api.minecraft.data.StructuredDataKey;\nimport com.viaversion.viaversion.api.minecraft.item.Item;\nimport com.viaversion.viaversion.api.minecraft.item.data.Consumable1_21_2;\nimport com.viaversion.viaversion.api.minecraft.item.data.DeathProtection;\nimport com.viaversion.viaversion.api.minecraft.item.data.Enchantable;\nimport com.viaversion.viaversion.api.minecraft.item.data.Enchantments;\nimport com.viaversion.viaversion.api.minecraft.item.data.Equippable;\nimport com.viaversion.viaversion.api.minecraft.item.data.FoodProperties1_20_5;\nimport com.viaversion.viaversion.api.minecraft.item.data.Instrument1_21_2;\nimport com.viaversion.viaversion.api.minecraft.item.data.ItemModel;\nimport com.viaversion.viaversion.api.minecraft.item.data.PotionEffect;\nimport com.viaversion.viaversion.api.minecraft.item.data.PotionEffectData;\nimport com.viaversion.viaversion.api.minecraft.item.data.Repairable;\nimport com.viaversion.viaversion.api.minecraft.item.data.UseCooldown;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.protocol.version.ProtocolVersion;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.version.VersionedTypes;\nimport com.viaversion.viaversion.protocols.v1_20_3to1_20_5.packet.ServerboundPacket1_20_5;\nimport com.viaversion.viaversion.protocols.v1_20_3to1_20_5.packet.ServerboundPackets1_20_5;\nimport com.viaversion.viaversion.protocols.v1_20_5to1_21.packet.ClientboundPackets1_21;\nimport com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ClientboundPacket1_21_2;\nimport com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ClientboundPackets1_21_2;\nimport com.viaversion.viaversion.rewriter.SoundRewriter;\nimport com.viaversion.viaversion.util.Key;\nimport com.viaversion.viaversion.util.Limit;\nimport com.viaversion.viaversion.util.Unit;\n\nimport static com.viaversion.viaversion.protocols.v1_21to1_21_2.rewriter.BlockItemPacketRewriter1_21_2.downgradeItemData;\nimport static com.viaversion.viaversion.protocols.v1_21to1_21_2.rewriter.BlockItemPacketRewriter1_21_2.updateItemData;\n\npublic final class BlockItemPacketRewriter1_21_2 extends BackwardsStructuredItemRewriter<ClientboundPacket1_21_2, ServerboundPacket1_20_5, Protocol1_21_2To1_21> {\n\n    public BlockItemPacketRewriter1_21_2(final Protocol1_21_2To1_21 protocol) {\n        super(protocol);\n    }\n\n    @Override\n    public void registerPackets() {\n        protocol.replaceClientbound(ClientboundPackets1_21_2.LEVEL_CHUNK_WITH_LIGHT, wrapper -> {\n            final Chunk chunk = protocol.getBlockRewriter().handleChunk1_18(wrapper);\n            protocol.getBlockRewriter().handleBlockEntities(chunk, wrapper.user());\n\n            if (!wrapper.user().getProtocolInfo().protocolVersion().equalTo(ProtocolVersion.v1_21)) {\n                return;\n            }\n\n            final EntityTracker tracker = wrapper.user().getEntityTracker(Protocol1_21_2To1_21.class);\n\n            final SignStorage storage = wrapper.user().get(SignStorage.class);\n            storage.removeSigns(chunk.getX(), chunk.getZ());\n\n            for (int i = 0; i < chunk.getSections().length; i++) {\n                final ChunkSection section = chunk.getSections()[i];\n\n                final DataPalette blockPalette = section.palette(PaletteType.BLOCKS);\n\n                boolean containsSign = false;\n                for (int idx = 0; idx < blockPalette.size(); idx++) {\n                    if (signBlockState(blockPalette.idByIndex(idx))) {\n                        containsSign = true;\n                        break;\n                    }\n                }\n\n                if (!containsSign) {\n                    continue;\n                }\n\n                for (int idx = 0; idx < ChunkSection.SIZE; idx++) {\n                    if (!signBlockState(blockPalette.idAt(idx))) {\n                        continue;\n                    }\n\n                    storage.addSign(new BlockPosition(\n                        ChunkSection.xFromIndex(idx) + (chunk.getX() << 4),\n                        ChunkSection.yFromIndex(idx) + tracker.currentMinY() + (i << 4),\n                        ChunkSection.zFromIndex(idx) + (chunk.getZ() << 4)\n                    ));\n                }\n            }\n        });\n\n        protocol.replaceClientbound(ClientboundPackets1_21_2.BLOCK_UPDATE, wrapper -> {\n            final BlockPosition position = wrapper.passthrough(Types.BLOCK_POSITION1_14);\n\n            final int blockId = wrapper.read(Types.VAR_INT);\n            final int mappedBlockId = protocol.getMappingData().getNewBlockStateId(blockId);\n            wrapper.write(Types.VAR_INT, mappedBlockId);\n\n            if (!wrapper.user().getProtocolInfo().protocolVersion().equalTo(ProtocolVersion.v1_21)) {\n                return;\n            }\n\n            final SignStorage storage = wrapper.user().get(SignStorage.class);\n            storage.removeSign(position);\n            if (signBlockState(mappedBlockId)) {\n                storage.addSign(position);\n            }\n        });\n\n        protocol.replaceClientbound(ClientboundPackets1_21_2.SECTION_BLOCKS_UPDATE, wrapper -> {\n            final long position = wrapper.passthrough(Types.LONG);\n\n            final int chunkX = (int) (position >> 42);\n            final int chunkY = (int) (position << 44 >> 44);\n            final int chunkZ = (int) (position << 22 >> 42);\n\n            final SignStorage signStorage = wrapper.user().get(SignStorage.class);\n\n            final boolean equalToV1_21 = wrapper.user().getProtocolInfo().protocolVersion().equalTo(ProtocolVersion.v1_21);\n\n            for (final BlockChangeRecord record : wrapper.passthrough(Types.VAR_LONG_BLOCK_CHANGE_ARRAY)) {\n                record.setBlockId(protocol.getMappingData().getNewBlockStateId(record.getBlockId()));\n\n                if (!equalToV1_21) {\n                    continue;\n                }\n\n                final int x = record.getSectionX() + (chunkX << 4);\n                final int y = record.getSectionY() + (chunkY << 4);\n                final int z = record.getSectionZ() + (chunkZ << 4);\n                final BlockPosition pos = new BlockPosition(x, y, z);\n                if (signBlockState(record.getBlockId())) {\n                    signStorage.addSign(pos);\n                } else {\n                    signStorage.removeSign(pos);\n                }\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_21_2.OPEN_SIGN_EDITOR, wrapper -> {\n            if (!wrapper.user().getProtocolInfo().protocolVersion().equalTo(ProtocolVersion.v1_21)) {\n                return;\n            }\n\n            final BlockPosition position = wrapper.passthrough(Types.BLOCK_POSITION1_14);\n            if (!wrapper.user().get(SignStorage.class).isSign(position)) {\n                wrapper.cancel();\n            }\n        });\n\n        protocol.replaceClientbound(ClientboundPackets1_21_2.COOLDOWN, wrapper -> {\n            final MappingData mappingData = protocol.getMappingData();\n            final String itemIdentifier = wrapper.read(Types.STRING);\n            final int id = mappingData.getFullItemMappings().id(itemIdentifier);\n            if (id != -1) {\n                final int mappedId = mappingData.getFullItemMappings().getNewId(id);\n                wrapper.write(Types.VAR_INT, mappedId);\n            } else {\n                wrapper.cancel();\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_21_2.SET_CURSOR_ITEM, ClientboundPackets1_21.CONTAINER_SET_SLOT, wrapper -> {\n            wrapper.write(Types.BYTE, (byte) -1); // Player inventory\n            wrapper.write(Types.VAR_INT, wrapper.user().get(InventoryStateIdStorage.class).stateId()); // State id; re-use the last known one\n            wrapper.write(Types.SHORT, (short) -1); // Cursor\n            passthroughClientboundItem(wrapper);\n        });\n\n        protocol.replaceClientbound(ClientboundPackets1_21_2.OPEN_SCREEN, wrapper -> {\n            wrapper.passthrough(Types.VAR_INT); // Id\n\n            final int containerType = wrapper.passthrough(Types.VAR_INT);\n            if (containerType == 21) {\n                // Track smithing table to remove new data\n                wrapper.user().get(InventoryStateIdStorage.class).setSmithingTableOpen(true);\n            }\n\n            protocol.getComponentRewriter().passthroughAndProcess(wrapper);\n        });\n\n        protocol.replaceClientbound(ClientboundPackets1_21_2.CONTAINER_SET_CONTENT, wrapper -> {\n            varIntToUnsignedByte(wrapper);\n\n            final int stateId = wrapper.passthrough(Types.VAR_INT);\n            wrapper.user().get(InventoryStateIdStorage.class).setStateId(stateId);\n\n            final Item[] items = wrapper.read(itemArrayType());\n            wrapper.write(mappedItemArrayType(), items);\n            for (int i = 0; i < items.length; i++) {\n                items[i] = handleItemToClient(wrapper.user(), items[i]);\n            }\n            passthroughClientboundItem(wrapper);\n        });\n        protocol.replaceClientbound(ClientboundPackets1_21_2.CONTAINER_SET_SLOT, wrapper -> {\n            varIntToByte(wrapper);\n\n            final int stateId = wrapper.passthrough(Types.VAR_INT);\n            wrapper.user().get(InventoryStateIdStorage.class).setStateId(stateId);\n\n            wrapper.passthrough(Types.SHORT); // Slot id\n            passthroughClientboundItem(wrapper);\n        });\n        protocol.registerClientbound(ClientboundPackets1_21_2.CONTAINER_SET_DATA, wrapper -> {\n            varIntToUnsignedByte(wrapper);\n\n            if (wrapper.user().get(InventoryStateIdStorage.class).smithingTableOpen()) {\n                // Cancel new data for smithing table\n                wrapper.cancel();\n            }\n        });\n        protocol.registerClientbound(ClientboundPackets1_21_2.CONTAINER_CLOSE, wrapper -> {\n            varIntToUnsignedByte(wrapper);\n            wrapper.user().get(InventoryStateIdStorage.class).setSmithingTableOpen(false);\n        });\n        protocol.registerClientbound(ClientboundPackets1_21_2.SET_HELD_SLOT, ClientboundPackets1_21.SET_CARRIED_ITEM);\n        protocol.registerClientbound(ClientboundPackets1_21_2.HORSE_SCREEN_OPEN, this::varIntToUnsignedByte);\n        protocol.registerServerbound(ServerboundPackets1_20_5.CONTAINER_CLOSE, wrapper -> {\n            byteToVarInt(wrapper);\n            wrapper.user().get(InventoryStateIdStorage.class).setSmithingTableOpen(false);\n        });\n        protocol.replaceServerbound(ServerboundPackets1_20_5.CONTAINER_CLICK, wrapper -> {\n            byteToVarInt(wrapper);\n            wrapper.passthrough(Types.VAR_INT); // State id\n            wrapper.passthrough(Types.SHORT); // Slot\n            wrapper.passthrough(Types.BYTE); // Button\n            wrapper.passthrough(Types.VAR_INT); // Mode\n            final int length = Limit.max(wrapper.passthrough(Types.VAR_INT), 128);\n            for (int i = 0; i < length; i++) {\n                wrapper.passthrough(Types.SHORT); // Slot\n                wrapper.write(itemType(), handleItemToServer(wrapper.user(), wrapper.read(mappedItemType())));\n            }\n            wrapper.write(itemType(), handleItemToServer(wrapper.user(), wrapper.read(mappedItemType())));\n        });\n\n        protocol.registerServerbound(ServerboundPackets1_20_5.USE_ITEM_ON, wrapper -> {\n            wrapper.passthrough(Types.VAR_INT); // Hand\n            wrapper.passthrough(Types.BLOCK_POSITION1_14); // Block position\n            wrapper.passthrough(Types.VAR_INT); // Direction\n            wrapper.passthrough(Types.FLOAT); // X\n            wrapper.passthrough(Types.FLOAT); // Y\n            wrapper.passthrough(Types.FLOAT); // Z\n            wrapper.passthrough(Types.BOOLEAN); // Inside\n            wrapper.write(Types.BOOLEAN, false); // World border hit\n        });\n        protocol.replaceClientbound(ClientboundPackets1_21_2.EXPLODE, wrapper -> {\n            wrapper.passthrough(Types.DOUBLE); // Center X\n            wrapper.passthrough(Types.DOUBLE); // Center Y\n            wrapper.passthrough(Types.DOUBLE); // Center Z\n\n            // The server will already send block changes separately\n            wrapper.write(Types.FLOAT, 0F); // Power\n            wrapper.write(Types.VAR_INT, 0); // No blocks affected\n\n            double knockbackX = 0;\n            double knockbackY = 0;\n            double knockbackZ = 0;\n            if (wrapper.read(Types.BOOLEAN)) {\n                knockbackX = wrapper.read(Types.DOUBLE);\n                knockbackY = wrapper.read(Types.DOUBLE);\n                knockbackZ = wrapper.read(Types.DOUBLE);\n            }\n            wrapper.write(Types.FLOAT, (float) knockbackX);\n            wrapper.write(Types.FLOAT, (float) knockbackY);\n            wrapper.write(Types.FLOAT, (float) knockbackZ);\n\n            wrapper.write(Types.VAR_INT, 0); // Block interaction type\n\n            final Particle explosionParticle = wrapper.read(VersionedTypes.V1_21.particle());\n            protocol.getParticleRewriter().rewriteParticle(wrapper.user(), explosionParticle);\n            // As small and large explosion particle\n            wrapper.write(VersionedTypes.V1_21_2.particle(), explosionParticle);\n            wrapper.write(VersionedTypes.V1_21_2.particle(), explosionParticle);\n\n            new SoundRewriter<>(protocol).soundHolderHandler().handle(wrapper);\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_21_2.RECIPE_BOOK_ADD, null, wrapper -> {\n            final RecipeStorage recipeStorage = wrapper.user().get(RecipeStorage.class);\n            final int size = wrapper.read(Types.VAR_INT);\n            for (int i = 0; i < size; i++) {\n                recipeStorage.readRecipe(wrapper);\n            }\n\n            final boolean replace = wrapper.read(Types.BOOLEAN);\n            if (replace) {\n                recipeStorage.clearRecipes();\n            }\n\n            recipeStorage.sendRecipes(wrapper.user());\n            wrapper.cancel();\n        });\n        protocol.registerClientbound(ClientboundPackets1_21_2.RECIPE_BOOK_REMOVE, ClientboundPackets1_21.RECIPE, wrapper -> {\n            final RecipeStorage recipeStorage = wrapper.user().get(RecipeStorage.class);\n            final int[] ids = wrapper.read(Types.VAR_INT_ARRAY_PRIMITIVE);\n            recipeStorage.lockRecipes(wrapper, ids);\n        });\n        protocol.registerClientbound(ClientboundPackets1_21_2.RECIPE_BOOK_SETTINGS, null, wrapper -> {\n            final RecipeStorage recipeStorage = wrapper.user().get(RecipeStorage.class);\n            final boolean[] settings = new boolean[RecipeStorage.RECIPE_BOOK_SETTINGS];\n            for (int i = 0; i < RecipeStorage.RECIPE_BOOK_SETTINGS; i++) {\n                settings[i] = wrapper.read(Types.BOOLEAN);\n            }\n            recipeStorage.setRecipeBookSettings(settings);\n\n            wrapper.cancel();\n        });\n        protocol.registerClientbound(ClientboundPackets1_21_2.UPDATE_RECIPES, wrapper -> {\n            // Inputs for furnaces etc. Old clients get these from the full recipes\n            final int size = wrapper.passthrough(Types.VAR_INT);\n            for (int i = 0; i < size; i++) {\n                wrapper.read(Types.STRING); // Recipe group\n                wrapper.read(Types.VAR_INT_ARRAY_PRIMITIVE); // Items\n            }\n\n            final RecipeStorage recipeStorage = wrapper.user().get(RecipeStorage.class);\n            recipeStorage.readStoneCutterRecipes(wrapper);\n\n            // Send later with the recipe book init\n            wrapper.cancel();\n        });\n        protocol.registerClientbound(ClientboundPackets1_21_2.PLACE_GHOST_RECIPE, wrapper -> {\n            this.varIntToByte(wrapper);\n            wrapper.cancel(); // Full recipe display, this doesn't look mappable\n        });\n        protocol.registerServerbound(ServerboundPackets1_20_5.PLACE_RECIPE, wrapper -> {\n            this.byteToVarInt(wrapper);\n\n            final String recipe = Key.stripMinecraftNamespace(wrapper.read(Types.STRING));\n            wrapper.write(Types.VAR_INT, Integer.parseInt(recipe));\n        });\n        protocol.registerServerbound(ServerboundPackets1_20_5.RECIPE_BOOK_SEEN_RECIPE, wrapper -> {\n            final String recipe = Key.stripMinecraftNamespace(wrapper.read(Types.STRING));\n            wrapper.write(Types.VAR_INT, Integer.parseInt(recipe));\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_21_2.SET_PLAYER_INVENTORY, ClientboundPackets1_21.CONTAINER_SET_SLOT, wrapper -> {\n            wrapper.write(Types.BYTE, (byte) -2); // Player inventory\n            wrapper.write(Types.VAR_INT, 0); // 0 state id\n            final int slot = wrapper.read(Types.VAR_INT);\n            wrapper.write(Types.SHORT, (short) slot);\n            passthroughClientboundItem(wrapper);\n        });\n    }\n\n    private void varIntToUnsignedByte(final PacketWrapper wrapper) {\n        final int containerId = wrapper.read(Types.VAR_INT);\n        wrapper.write(Types.UNSIGNED_BYTE, (short) containerId);\n    }\n\n    private void varIntToByte(final PacketWrapper wrapper) {\n        final int containerId = wrapper.read(Types.VAR_INT);\n        wrapper.write(Types.BYTE, (byte) containerId);\n    }\n\n    private void byteToVarInt(final PacketWrapper wrapper) {\n        final byte containerId = wrapper.read(Types.BYTE);\n        wrapper.write(Types.VAR_INT, (int) containerId);\n    }\n\n    private boolean signBlockState(final int blockStateId) {\n        return (blockStateId >= 4302 && blockStateId <= 4589) // Normal signs\n            || (blockStateId >= 4762 && blockStateId <= 5625) // Wall & Hanging signs\n            || (blockStateId >= 19276 && blockStateId <= 19355); // Warped & Crimson signs\n    }\n\n    @Override\n    public Item handleItemToClient(final UserConnection connection, Item item) {\n        backupInconvertibleData(item);\n        item = super.handleItemToClient(connection, item);\n        downgradeItemData(item);\n        return item;\n    }\n\n    @Override\n    public Item handleItemToServer(final UserConnection connection, Item item) {\n        item = super.handleItemToServer(connection, item);\n\n        // Handle food properties item manually here - the only protocol that has it\n        // The other way around it's handled by the super handleItemToClient method\n        final StructuredDataContainer data = item.dataContainer();\n        final FoodProperties1_20_5 food = data.get(StructuredDataKey.FOOD1_21);\n        if (food != null && food.usingConvertsTo() != null) {\n            this.handleItemToServer(connection, food.usingConvertsTo());\n        }\n\n        updateItemData(item);\n        restoreInconvertibleData(item);\n\n        // Enchantments with level 0 are not supported anymore, if an older client for some reason sends it (for example\n        // using stored items in the hotbar lists), we need to remove it.\n        final Enchantments enchantments = data.get(StructuredDataKey.ENCHANTMENTS1_20_5);\n        if (enchantments != null) {\n            // The enchantments might be used to create a glint, set glint override to true if the enchantments are empty after filtering\n            final boolean removed = enchantments.enchantments().values().removeIf(level -> level == 0);\n            if (removed && enchantments.size() == 0) {\n                data.set(StructuredDataKey.ENCHANTMENT_GLINT_OVERRIDE, true);\n            }\n        }\n        final Enchantments storedEnchantments = data.get(StructuredDataKey.STORED_ENCHANTMENTS1_20_5);\n        if (storedEnchantments != null) {\n            storedEnchantments.enchantments().values().removeIf(level -> level == 0);\n        }\n        return item;\n    }\n\n    // Backup inconvertible data and later restore to prevent data loss for creative mode clients\n    private void backupInconvertibleData(final Item item) {\n        final StructuredDataContainer data = item.dataContainer();\n        data.setIdLookup(protocol, true);\n\n        final CompoundTag backupTag = new CompoundTag();\n\n        final Holder<Instrument1_21_2> instrument = data.get(StructuredDataKey.INSTRUMENT1_21_2);\n        if (instrument != null && instrument.isDirect()) {\n            backupTag.put(\"instrument_description\", instrument.value().description());\n        }\n\n        final Repairable repairable = data.get(StructuredDataKey.REPAIRABLE);\n        if (repairable != null) {\n            backupTag.put(\"repairable\", holderSetToTag(repairable.items()));\n        }\n\n        final Enchantable enchantable = data.get(StructuredDataKey.ENCHANTABLE);\n        if (enchantable != null) {\n            backupTag.putInt(\"enchantable\", enchantable.value());\n        }\n\n        final UseCooldown useCooldown = data.get(StructuredDataKey.USE_COOLDOWN);\n        if (useCooldown != null) {\n            final CompoundTag tag = new CompoundTag();\n            tag.putFloat(\"seconds\", useCooldown.seconds());\n            if (useCooldown.cooldownGroup() != null) {\n                tag.putString(\"cooldown_group\", useCooldown.cooldownGroup());\n            }\n            backupTag.put(\"use_cooldown\", tag);\n        }\n\n        final ItemModel itemModel = data.get(StructuredDataKey.ITEM_MODEL);\n        if (itemModel != null) {\n            backupTag.putString(\"item_model\", itemModel.key().original());\n        }\n\n        final Equippable equippable = data.get(StructuredDataKey.EQUIPPABLE1_21_2);\n        if (equippable != null) {\n            final CompoundTag tag = new CompoundTag();\n\n            tag.putInt(\"equipment_slot\", equippable.equipmentSlot());\n            saveSoundEventHolder(tag, equippable.soundEvent());\n            final String model = equippable.model();\n            if (model != null) {\n                tag.putString(\"model\", model);\n            }\n            final String cameraOverlay = equippable.cameraOverlay();\n            if (cameraOverlay != null) {\n                tag.putString(\"camera_overlay\", cameraOverlay);\n            }\n            if (equippable.allowedEntities() != null) {\n                tag.put(\"allowed_entities\", holderSetToTag(equippable.allowedEntities()));\n            }\n            tag.putBoolean(\"dispensable\", equippable.dispensable());\n            tag.putBoolean(\"swappable\", equippable.swappable());\n            tag.putBoolean(\"damage_on_hurt\", equippable.damageOnHurt());\n\n            backupTag.put(\"equippable\", tag);\n        }\n\n        final Unit glider = data.get(StructuredDataKey.GLIDER);\n        if (glider != null) {\n            backupTag.putBoolean(\"glider\", true);\n        }\n\n        final Key tooltipStyle = data.get(StructuredDataKey.TOOLTIP_STYLE);\n        if (tooltipStyle != null) {\n            backupTag.putString(\"tooltip_style\", tooltipStyle.original());\n        }\n\n        final DeathProtection deathProtection = data.get(StructuredDataKey.DEATH_PROTECTION);\n        if (deathProtection != null) {\n            final ListTag<CompoundTag> tag = new ListTag<>(CompoundTag.class);\n            for (final Consumable1_21_2.ConsumeEffect<?> effect : deathProtection.deathEffects()) {\n                final CompoundTag effectTag = new CompoundTag();\n                convertConsumableEffect(effectTag, effect);\n                tag.add(effectTag);\n            }\n            backupTag.put(\"death_protection\", tag);\n        }\n\n        if (!backupTag.isEmpty()) {\n            saveTag(createCustomTag(item), backupTag, \"inconvertible_data\");\n        }\n    }\n\n    private void convertConsumableEffect(final CompoundTag tag, Consumable1_21_2.ConsumeEffect<?> effect) {\n        tag.putInt(\"id\", effect.id());\n        if (effect.type() == Consumable1_21_2.ApplyStatusEffects.TYPE && effect.value() instanceof Consumable1_21_2.ApplyStatusEffects value) {\n            tag.putString(\"type\", \"apply_effects\");\n\n            final ListTag<CompoundTag> effects = new ListTag<>(CompoundTag.class);\n            for (final PotionEffect potionEffect : value.effects()) {\n                final CompoundTag effectTag = new CompoundTag();\n                effectTag.putInt(\"effect\", potionEffect.effect());\n                convertPotionEffectData(effectTag, potionEffect.effectData());\n                effects.add(effectTag);\n            }\n            tag.put(\"effects\", effects);\n            tag.putFloat(\"probability\", value.probability());\n        } else if (effect.type() == Types.HOLDER_SET && effect.value() instanceof HolderSet set) {\n            tag.putString(\"type\", \"remove_effects\");\n\n            tag.put(\"remove_effects\", holderSetToTag(set));\n        } else if (effect.type() == Types.EMPTY) {\n            tag.putString(\"type\", \"clear_all_effects\");\n        } else if (effect.type() == Types.FLOAT) {\n            tag.putString(\"type\", \"teleport_randomly\");\n\n            tag.putFloat(\"probability\", (Float) effect.value());\n        } else if (effect.type() == Types.SOUND_EVENT && effect.value() instanceof Holder sound) {\n            tag.putString(\"type\", \"play_sound\");\n\n            saveSoundEventHolder(tag, sound);\n        }\n    }\n\n    private void convertPotionEffectData(final CompoundTag tag, final PotionEffectData data) {\n        tag.putInt(\"amplifier\", data.amplifier());\n        tag.putInt(\"duration\", data.duration());\n        tag.putBoolean(\"ambient\", data.ambient());\n        tag.putBoolean(\"show_particles\", data.showParticles());\n        tag.putBoolean(\"show_icon\", data.showIcon());\n        if (data.hiddenEffect() != null) {\n            final CompoundTag hiddenEffect = new CompoundTag();\n            convertPotionEffectData(hiddenEffect, data.hiddenEffect());\n            tag.put(\"hidden_effect\", hiddenEffect);\n        }\n    }\n\n    private Consumable1_21_2.ConsumeEffect<?> convertConsumableEffect(final CompoundTag tag) {\n        final int id = tag.getInt(\"id\");\n        final String type = tag.getString(\"type\");\n        if (\"apply_effects\".equals(type)) {\n            final ListTag<CompoundTag> effects = tag.getListTag(\"effects\", CompoundTag.class);\n            final PotionEffect[] potionEffects = new PotionEffect[effects.size()];\n            for (int i = 0; i < effects.size(); i++) {\n                final CompoundTag effectTag = effects.get(i);\n                final int effect = effectTag.getInt(\"effect\");\n                final PotionEffectData data = convertPotionEffectData(effectTag);\n                potionEffects[i] = new PotionEffect(effect, data);\n            }\n            final float probability = tag.getFloat(\"probability\");\n            return new Consumable1_21_2.ConsumeEffect<>(id, Consumable1_21_2.ApplyStatusEffects.TYPE, new Consumable1_21_2.ApplyStatusEffects(potionEffects, probability));\n        } else if (\"remove_effects\".equals(type)) {\n            final HolderSet set = restoreHolderSet(tag, \"remove_effects\");\n            return new Consumable1_21_2.ConsumeEffect<>(id, Types.HOLDER_SET, set);\n        } else if (\"clear_all_effects\".equals(type)) {\n            return new Consumable1_21_2.ConsumeEffect<>(id, Types.EMPTY, Unit.INSTANCE);\n        } else if (\"teleport_randomly\".equals(type)) {\n            final float probability = tag.getFloat(\"probability\");\n            return new Consumable1_21_2.ConsumeEffect<>(id, Types.FLOAT, probability);\n        } else if (\"play_sound\".equals(type)) {\n            final Holder<SoundEvent> sound = restoreSoundEventHolder(tag);\n            return new Consumable1_21_2.ConsumeEffect<>(id, Types.SOUND_EVENT, sound);\n        }\n        return null;\n    }\n\n    private PotionEffectData convertPotionEffectData(final CompoundTag tag) {\n        final int amplifier = tag.getInt(\"amplifier\");\n        final int duration = tag.getInt(\"duration\");\n        final boolean ambient = tag.getBoolean(\"ambient\");\n        final boolean showParticles = tag.getBoolean(\"show_particles\");\n        final boolean showIcon = tag.getBoolean(\"show_icon\");\n        final CompoundTag hiddenEffect = tag.getCompoundTag(\"hidden_effect\");\n        return new PotionEffectData(amplifier, duration, ambient, showParticles, showIcon, hiddenEffect != null ? convertPotionEffectData(hiddenEffect) : null);\n    }\n\n    private void restoreInconvertibleData(final Item item) {\n        final StructuredDataContainer data = item.dataContainer();\n        final CompoundTag customData = data.get(StructuredDataKey.CUSTOM_DATA);\n        if (customData == null || !(customData.remove(nbtTagName(\"inconvertible_data\")) instanceof CompoundTag backupTag)) {\n            return;\n        }\n\n        final Holder<Instrument1_21_2> instrument = data.get(StructuredDataKey.INSTRUMENT1_21_2);\n        if (instrument != null && instrument.isDirect()) {\n            final Tag description = backupTag.get(\"instrument_description\");\n            if (description != null) {\n                final Instrument1_21_2 delegate = instrument.value();\n                data.set(StructuredDataKey.INSTRUMENT1_21_2, Holder.of(new Instrument1_21_2(delegate.soundEvent(), delegate.useDuration(), delegate.range(), description)));\n            }\n        }\n\n        if (backupTag.contains(\"repairable\")) {\n            data.set(StructuredDataKey.REPAIRABLE, new Repairable(restoreHolderSet(backupTag, \"repairable\")));\n        }\n\n        final IntTag enchantable = backupTag.getIntTag(\"enchantable\");\n        if (enchantable != null) {\n            data.set(StructuredDataKey.ENCHANTABLE, new Enchantable(enchantable.asInt()));\n        }\n\n        final CompoundTag useCooldown = backupTag.getCompoundTag(\"use_cooldown\");\n        if (useCooldown != null) {\n            final float seconds = useCooldown.getFloat(\"seconds\");\n            final String cooldownGroup = useCooldown.getString(\"cooldown_group\");\n            data.set(StructuredDataKey.USE_COOLDOWN, new UseCooldown(seconds, cooldownGroup));\n        }\n\n        final String itemModel = backupTag.getString(\"item_model\");\n        if (itemModel != null) {\n            data.set(StructuredDataKey.ITEM_MODEL, new ItemModel(Key.of(itemModel)));\n        }\n\n        final CompoundTag equippable = backupTag.getCompoundTag(\"equippable\");\n        if (equippable != null) {\n            final int equipmentSlot = equippable.getInt(\"equipment_slot\");\n            final Holder<SoundEvent> soundEvent = restoreSoundEventHolder(equippable);\n            final String model = equippable.getString(\"model\");\n            final String cameraOverlay = equippable.getString(\"camera_overlay\");\n            final HolderSet allowedEntities = equippable.contains(\"allowed_entities\") ? restoreHolderSet(equippable, \"allowed_entities\") : null;\n            final boolean dispensable = equippable.getBoolean(\"dispensable\");\n            final boolean swappable = equippable.getBoolean(\"swappable\");\n            final boolean damageOnHurt = equippable.getBoolean(\"damage_on_hurt\");\n            data.set(StructuredDataKey.EQUIPPABLE1_21_2, new Equippable(equipmentSlot, soundEvent, model, cameraOverlay, allowedEntities, dispensable, swappable, damageOnHurt));\n        }\n\n        final ByteTag glider = backupTag.getByteTag(\"glider\");\n        if (glider != null) {\n            data.set(StructuredDataKey.GLIDER, Unit.INSTANCE);\n        }\n\n        final String tooltipStyle = backupTag.getString(\"tooltip_style\");\n        if (tooltipStyle != null) {\n            data.set(StructuredDataKey.TOOLTIP_STYLE, Key.of(tooltipStyle));\n        }\n\n        final ListTag<CompoundTag> deathProtection = backupTag.getListTag(\"death_protection\", CompoundTag.class);\n        if (deathProtection != null) {\n            final Consumable1_21_2.ConsumeEffect<?>[] effects = new Consumable1_21_2.ConsumeEffect[deathProtection.size()];\n            for (int i = 0; i < deathProtection.size(); i++) {\n                effects[i] = convertConsumableEffect(deathProtection.get(i));\n            }\n            data.set(StructuredDataKey.DEATH_PROTECTION, new DeathProtection(effects));\n        }\n\n        removeCustomTag(data, customData);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_2to1_21/rewriter/ComponentRewriter1_21_2.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_2to1_21.rewriter;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.viabackwards.api.rewriters.text.JsonNBTComponentRewriter;\nimport com.viaversion.viabackwards.protocol.v1_21_2to1_21.Protocol1_21_2To1_21;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.data.StructuredDataKey;\nimport com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ClientboundPacket1_21_2;\nimport com.viaversion.viaversion.protocols.v1_21to1_21_2.rewriter.BlockItemPacketRewriter1_21_2;\nimport com.viaversion.viaversion.util.SerializerVersion;\nimport com.viaversion.viaversion.util.TagUtil;\n\npublic final class ComponentRewriter1_21_2 extends JsonNBTComponentRewriter<ClientboundPacket1_21_2> {\n\n    public ComponentRewriter1_21_2(final Protocol1_21_2To1_21 protocol) {\n        super(protocol, ReadType.NBT);\n    }\n\n    @Override\n    protected void handleShowItem(final UserConnection connection, final CompoundTag itemTag, final CompoundTag componentsTag) {\n        super.handleShowItem(connection, itemTag, componentsTag);\n        if (componentsTag == null) {\n            return;\n        }\n\n        com.viaversion.viaversion.protocols.v1_21to1_21_2.rewriter.ComponentRewriter1_21_2.convertAttributes(componentsTag, protocol.getMappingData().getAttributeMappings());\n\n        final CompoundTag instrument = TagUtil.getNamespacedCompoundTag(componentsTag, \"instrument\");\n        if (instrument != null) {\n            instrument.remove(\"description\");\n        }\n\n        final CompoundTag useRemainder = TagUtil.getNamespacedCompoundTag(componentsTag, \"use_remainder\");\n        final CompoundTag food = TagUtil.getNamespacedCompoundTag(componentsTag, \"food\");\n        if (food != null) {\n            if (useRemainder != null) {\n                food.put(\"using_converts_to\", useRemainder);\n            }\n            food.putFloat(\"eat_seconds\", 1.6F);\n        }\n\n        removeDataComponents(componentsTag, BlockItemPacketRewriter1_21_2.NEW_DATA_TO_REMOVE);\n        removeDataComponents(componentsTag, StructuredDataKey.DAMAGE_RESISTANT1_21_2, StructuredDataKey.LOCK1_21_2); // No point in updating these\n    }\n\n    @Override\n    protected SerializerVersion inputSerializerVersion() {\n        return SerializerVersion.V1_20_5;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_2to1_21/rewriter/EntityPacketRewriter1_21_2.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_2to1_21.rewriter;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.FloatTag;\nimport com.viaversion.nbt.tag.IntTag;\nimport com.viaversion.nbt.tag.ListTag;\nimport com.viaversion.nbt.tag.Tag;\nimport com.viaversion.viabackwards.ViaBackwards;\nimport com.viaversion.viabackwards.api.rewriters.EntityRewriter;\nimport com.viaversion.viabackwards.protocol.v1_21_2to1_21.Protocol1_21_2To1_21;\nimport com.viaversion.viabackwards.protocol.v1_21_2to1_21.storage.PlayerStorage;\nimport com.viaversion.viabackwards.protocol.v1_21_2to1_21.storage.SignStorage;\nimport com.viaversion.viabackwards.utils.VelocityUtil;\nimport com.viaversion.viaversion.api.data.entity.EntityTracker;\nimport com.viaversion.viaversion.api.minecraft.Holder;\nimport com.viaversion.viaversion.api.minecraft.Particle;\nimport com.viaversion.viaversion.api.minecraft.RegistryEntry;\nimport com.viaversion.viaversion.api.minecraft.SoundEvent;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityType;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_21_2;\nimport com.viaversion.viaversion.api.minecraft.entitydata.EntityData;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.version.VersionedTypes;\nimport com.viaversion.viaversion.protocols.v1_20_3to1_20_5.packet.ServerboundPackets1_20_5;\nimport com.viaversion.viaversion.protocols.v1_20_5to1_21.packet.ClientboundConfigurationPackets1_21;\nimport com.viaversion.viaversion.protocols.v1_20_5to1_21.packet.ClientboundPackets1_21;\nimport com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ClientboundPacket1_21_2;\nimport com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ClientboundPackets1_21_2;\nimport com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ServerboundPackets1_21_2;\nimport com.viaversion.viaversion.protocols.v1_21to1_21_2.storage.ClientVehicleStorage;\nimport com.viaversion.viaversion.util.Key;\nimport java.util.ArrayList;\nimport java.util.BitSet;\nimport java.util.List;\nimport org.checkerframework.checker.nullness.qual.Nullable;\n\npublic final class EntityPacketRewriter1_21_2 extends EntityRewriter<ClientboundPacket1_21_2, Protocol1_21_2To1_21> {\n\n    private static final int REL_X = 0;\n    private static final int REL_Y = 1;\n    private static final int REL_Z = 2;\n    private static final int REL_Y_ROT = 3;\n    private static final int REL_X_ROT = 4;\n    private static final int REL_DELTA_X = 5;\n    private static final int REL_DELTA_Y = 6;\n    private static final int REL_DELTA_Z = 7;\n    private static final int REL_ROTATE_DELTA = 8;\n    private boolean warned = ViaBackwards.getConfig().suppressEmulationWarnings();\n\n    public EntityPacketRewriter1_21_2(final Protocol1_21_2To1_21 protocol) {\n        super(protocol, VersionedTypes.V1_21.entityDataTypes.optionalComponentType, VersionedTypes.V1_21.entityDataTypes.booleanType);\n    }\n\n    @Override\n    public void registerPackets() {\n        protocol.replaceClientbound(ClientboundPackets1_21_2.ADD_ENTITY, wrapper -> {\n            final int entityId = wrapper.passthrough(Types.VAR_INT);\n            wrapper.passthrough(Types.UUID); // Entity UUID\n            final int entityTypeId = wrapper.passthrough(Types.VAR_INT);\n            wrapper.passthrough(Types.DOUBLE); // X\n            wrapper.passthrough(Types.DOUBLE); // Y\n            wrapper.passthrough(Types.DOUBLE); // Z\n            wrapper.passthrough(Types.BYTE); // Pitch\n            wrapper.passthrough(Types.BYTE); // Yaw\n            wrapper.passthrough(Types.BYTE); // Head yaw\n            wrapper.passthrough(Types.VAR_INT); // Data\n            getSpawnTrackerWithDataHandler1_19().handle(wrapper);\n\n            final EntityType type = EntityTypes1_21_2.getTypeFromId(entityTypeId);\n            if (type.isOrHasParent(EntityTypes1_21_2.ABSTRACT_BOAT)) {\n                wrapper.send(Protocol1_21_2To1_21.class);\n                wrapper.cancel();\n\n                // Add boat type to entity data\n                final List<EntityData> data = new ArrayList<>();\n                final int boatType = type.isOrHasParent(EntityTypes1_21_2.ABSTRACT_CHEST_BOAT) ? chestBoatTypeFromEntityType(type) : boatTypeFromEntityType(type);\n                data.add(new EntityData(11, VersionedTypes.V1_21.entityDataTypes.varIntType, boatType));\n\n                final PacketWrapper entityDataPacket = wrapper.create(ClientboundPackets1_21.SET_ENTITY_DATA);\n                entityDataPacket.write(Types.VAR_INT, entityId);\n                entityDataPacket.write(VersionedTypes.V1_21.entityDataList, data);\n                entityDataPacket.send(Protocol1_21_2To1_21.class);\n            }\n        });\n\n        protocol.getRegistryDataRewriter().addEnchantmentEffectRewriter(\"change_item_damage\", tag -> tag.putString(\"type\", \"damage_item\"));\n        protocol.replaceClientbound(ClientboundConfigurationPackets1_21.REGISTRY_DATA, wrapper -> {\n            final String registryKey = Key.stripMinecraftNamespace(wrapper.passthrough(Types.STRING));\n            final RegistryEntry[] entries = wrapper.read(Types.REGISTRY_ENTRY_ARRAY);\n            if (registryKey.equals(\"instrument\")) {\n                wrapper.cancel();\n                return;\n            }\n\n            if (registryKey.equals(\"worldgen/biome\")) {\n                for (final RegistryEntry entry : entries) {\n                    if (entry.tag() == null) {\n                        continue;\n                    }\n\n                    final CompoundTag effects = ((CompoundTag) entry.tag()).getCompoundTag(\"effects\");\n                    final CompoundTag particle = effects.getCompoundTag(\"particle\");\n                    if (particle == null) {\n                        continue;\n                    }\n\n                    final CompoundTag particleOptions = particle.getCompoundTag(\"options\");\n                    final String particleType = particleOptions.getString(\"type\");\n                    updateParticleFormat(particleOptions, Key.stripMinecraftNamespace(particleType));\n                }\n            }\n\n            wrapper.write(Types.REGISTRY_ENTRY_ARRAY, protocol.getRegistryDataRewriter().handle(wrapper.user(), registryKey, entries));\n        });\n\n        protocol.replaceClientbound(ClientboundPackets1_21_2.LOGIN, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.INT); // Entity id\n                map(Types.BOOLEAN); // Hardcore\n                map(Types.STRING_ARRAY); // World List\n                map(Types.VAR_INT); // Max players\n                map(Types.VAR_INT); // View distance\n                map(Types.VAR_INT); // Simulation distance\n                map(Types.BOOLEAN); // Reduced debug info\n                map(Types.BOOLEAN); // Show death screen\n                map(Types.BOOLEAN); // Limited crafting\n                map(Types.VAR_INT); // Dimension key\n                map(Types.STRING); // World\n                map(Types.LONG); // Seed\n                map(Types.BYTE); // Gamemode\n                map(Types.BYTE); // Previous gamemode\n                map(Types.BOOLEAN); // Debug\n                map(Types.BOOLEAN); // Flat\n                map(Types.OPTIONAL_GLOBAL_POSITION); // Last death location\n                map(Types.VAR_INT); // Portal cooldown\n                handler(worldDataTrackerHandlerByKey1_20_5(3));\n                handler(playerTrackerHandler());\n                read(Types.VAR_INT); // Sea level\n            }\n        });\n\n        protocol.replaceClientbound(ClientboundPackets1_21_2.RESPAWN, wrapper -> {\n            final int dimensionId = wrapper.passthrough(Types.VAR_INT);\n            final String world = wrapper.passthrough(Types.STRING);\n            wrapper.passthrough(Types.LONG); // Seed\n            wrapper.passthrough(Types.BYTE); // Gamemode\n            wrapper.passthrough(Types.BYTE); // Previous gamemode\n            wrapper.passthrough(Types.BOOLEAN); // Debug\n            wrapper.passthrough(Types.BOOLEAN); // Flat\n            wrapper.passthrough(Types.OPTIONAL_GLOBAL_POSITION); // Last death location\n            wrapper.passthrough(Types.VAR_INT); // Portal cooldown\n\n            wrapper.read(Types.VAR_INT); // Sea level\n\n            final EntityTracker tracker = tracker(wrapper.user());\n            if (tracker.currentWorld() != null && !tracker.currentWorld().equals(world)) {\n                wrapper.user().put(new SignStorage());\n            }\n            trackWorldDataByKey1_20_5(wrapper.user(), dimensionId, world);\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_21_2.ENTITY_POSITION_SYNC, ClientboundPackets1_21.TELEPORT_ENTITY, wrapper -> {\n            wrapper.passthrough(Types.VAR_INT); // Entity ID\n            wrapper.passthrough(Types.DOUBLE); // X\n            wrapper.passthrough(Types.DOUBLE); // Y\n            wrapper.passthrough(Types.DOUBLE); // Z\n\n            // Unused\n            wrapper.read(Types.DOUBLE); // Delta movement X\n            wrapper.read(Types.DOUBLE); // Delta movement Y\n            wrapper.read(Types.DOUBLE); // Delta movement Z\n\n            final float yaw = wrapper.read(Types.FLOAT);\n            final float pitch = wrapper.read(Types.FLOAT);\n            writePackedRotation(wrapper, yaw, pitch);\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_21_2.PLAYER_ROTATION, ClientboundPackets1_21.PLAYER_LOOK_AT, wrapper -> {\n            final float yaw = wrapper.read(Types.FLOAT);\n            final float pitch = wrapper.read(Types.FLOAT);\n\n            final double yRadians = Math.toRadians(yaw);\n            final double xRadians = Math.toRadians(pitch);\n\n            final double factor = -Math.cos(-xRadians);\n            final double deltaX = Math.sin(-yRadians - (float) Math.PI) * factor;\n            final double deltaY = Math.sin(-xRadians);\n            final double deltaZ = Math.cos(-yRadians - (float) Math.PI) * factor;\n\n            final PlayerStorage storage = wrapper.user().get(PlayerStorage.class);\n            wrapper.write(Types.VAR_INT, 0); // From anchor\n            wrapper.write(Types.DOUBLE, storage.x() + deltaX); // X\n            wrapper.write(Types.DOUBLE, storage.y() + deltaY); // Y\n            wrapper.write(Types.DOUBLE, storage.z() + deltaZ); // Z\n            wrapper.write(Types.BOOLEAN, false); // At entity\n\n            final PacketWrapper entityMotionPacket = PacketWrapper.create(ServerboundPackets1_21_2.MOVE_PLAYER_ROT, wrapper.user());\n            entityMotionPacket.write(Types.FLOAT, yaw);\n            entityMotionPacket.write(Types.FLOAT, pitch);\n            entityMotionPacket.write(Types.UNSIGNED_BYTE, (short) 0); // On ground and horizontal collision\n            entityMotionPacket.sendToServer(Protocol1_21_2To1_21.class);\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_21_2.TELEPORT_ENTITY, wrapper -> {\n            final int entityId = wrapper.passthrough(Types.VAR_INT);\n            final double x = wrapper.passthrough(Types.DOUBLE);\n            final double y = wrapper.passthrough(Types.DOUBLE);\n            final double z = wrapper.passthrough(Types.DOUBLE);\n\n            final double movementX = wrapper.read(Types.DOUBLE);\n            final double movementY = wrapper.read(Types.DOUBLE);\n            final double movementZ = wrapper.read(Types.DOUBLE);\n\n            final float yaw = wrapper.read(Types.FLOAT);\n            final float pitch = wrapper.read(Types.FLOAT);\n            writePackedRotation(wrapper, yaw, pitch);\n\n            final int relativeArguments = wrapper.read(Types.INT);\n\n            // Send alongside separate entity motion\n            wrapper.send(Protocol1_21_2To1_21.class);\n            wrapper.cancel();\n\n            handleRelativeArguments(wrapper, x, y, z, yaw, pitch, relativeArguments, movementX, movementY, movementZ, entityId);\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_21_2.PLAYER_POSITION, wrapper -> {\n            final int teleportId = wrapper.read(Types.VAR_INT);\n\n            final double x = wrapper.passthrough(Types.DOUBLE);\n            final double y = wrapper.passthrough(Types.DOUBLE);\n            final double z = wrapper.passthrough(Types.DOUBLE);\n\n            final double movementX = wrapper.read(Types.DOUBLE);\n            final double movementY = wrapper.read(Types.DOUBLE);\n            final double movementZ = wrapper.read(Types.DOUBLE);\n\n            final float yaw = wrapper.passthrough(Types.FLOAT);\n            final float pitch = wrapper.passthrough(Types.FLOAT);\n\n            // Just keep the new values in there\n            final int relativeArguments = wrapper.read(Types.INT);\n            wrapper.write(Types.BYTE, (byte) relativeArguments);\n            wrapper.write(Types.VAR_INT, teleportId);\n\n            // Send alongside separate entity motion\n            wrapper.send(Protocol1_21_2To1_21.class);\n            wrapper.cancel();\n\n            handleRelativeArguments(wrapper, x, y, z, yaw, pitch, relativeArguments, movementX, movementY, movementZ, null);\n        });\n\n        protocol.registerServerbound(ServerboundPackets1_20_5.PLAYER_COMMAND, wrapper -> {\n            wrapper.passthrough(Types.VAR_INT);\n            final int action = wrapper.passthrough(Types.VAR_INT);\n\n            final PlayerStorage storage = wrapper.user().get(PlayerStorage.class);\n            if (action == 0) {\n                storage.setPlayerCommandTrackedSneaking(true);\n            } else if (action == 1) {\n                storage.setPlayerCommandTrackedSneaking(false);\n            } else if (action == 3) {\n                storage.setPlayerCommandTrackedSprinting(true);\n            } else if (action == 4) {\n                storage.setPlayerCommandTrackedSprinting(false);\n            }\n        });\n\n        // Now also sent by the player if not in a vehicle, but we can't emulate that here, and otherwise only used in predicates\n        protocol.registerServerbound(ServerboundPackets1_20_5.PLAYER_INPUT, wrapper -> {\n            final float sideways = wrapper.read(Types.FLOAT);\n            final float forward = wrapper.read(Types.FLOAT);\n            final byte flags = wrapper.read(Types.BYTE);\n\n            byte updatedFlags = 0;\n            if (forward > 0) {\n                updatedFlags |= 1; // Forward\n            } else if (forward < 0) {\n                updatedFlags |= 1 << 1; // Backward\n            }\n\n            if (sideways > 0) {\n                updatedFlags |= 1 << 2; // Left\n            } else if (sideways < 0) {\n                updatedFlags |= 1 << 3; // Right\n            }\n\n            if ((flags & 1) != 0) { // Jumping\n                updatedFlags |= 1 << 4;\n            }\n\n            final boolean sneaking = (flags & 2) != 0;\n            if (sneaking) {\n                updatedFlags |= 1 << 5;\n            }\n\n            // Sprinting we don't know...\n\n            wrapper.write(Types.BYTE, updatedFlags);\n\n            // Player input no longer sets the sneaking state on the server\n            // Send the change separately if needed (= when in a vehicle and player commands aren't sent by the old client)\n            sendSneakingPlayerCommand(wrapper, sneaking);\n        });\n\n        protocol.registerServerbound(ServerboundPackets1_20_5.MOVE_PLAYER_POS, wrapper -> {\n            final double x = wrapper.passthrough(Types.DOUBLE);\n            final double y = wrapper.passthrough(Types.DOUBLE);\n            final double z = wrapper.passthrough(Types.DOUBLE);\n            fixOnGround(wrapper);\n\n            final PlayerStorage storage = wrapper.user().get(PlayerStorage.class);\n            storage.setPosition(x, y, z);\n        });\n        protocol.registerServerbound(ServerboundPackets1_20_5.MOVE_PLAYER_POS_ROT, wrapper -> {\n            final double x = wrapper.passthrough(Types.DOUBLE);\n            final double y = wrapper.passthrough(Types.DOUBLE);\n            final double z = wrapper.passthrough(Types.DOUBLE);\n            final float yaw = wrapper.passthrough(Types.FLOAT);\n            final float pitch = wrapper.passthrough(Types.FLOAT);\n            fixOnGround(wrapper);\n\n            final PlayerStorage storage = wrapper.user().get(PlayerStorage.class);\n            storage.setPosition(x, y, z);\n            storage.setRotation(yaw, pitch);\n        });\n        protocol.registerServerbound(ServerboundPackets1_20_5.MOVE_PLAYER_ROT, wrapper -> {\n            final float yaw = wrapper.passthrough(Types.FLOAT);\n            final float pitch = wrapper.passthrough(Types.FLOAT);\n            fixOnGround(wrapper);\n\n            final PlayerStorage storage = wrapper.user().get(PlayerStorage.class);\n            storage.setRotation(yaw, pitch);\n        });\n        protocol.registerServerbound(ServerboundPackets1_20_5.MOVE_PLAYER_STATUS_ONLY, this::fixOnGround);\n        protocol.registerServerbound(ServerboundPackets1_20_5.MOVE_VEHICLE, wrapper -> {\n            final double x = wrapper.passthrough(Types.DOUBLE);\n            final double y = wrapper.passthrough(Types.DOUBLE);\n            final double z = wrapper.passthrough(Types.DOUBLE);\n            final float yaw = wrapper.passthrough(Types.FLOAT);\n            final float pitch = wrapper.passthrough(Types.FLOAT);\n\n            final PlayerStorage storage = wrapper.user().get(PlayerStorage.class);\n            storage.setPosition(x, y, z);\n            storage.setRotation(yaw, pitch);\n        });\n\n        protocol.replaceClientbound(ClientboundPackets1_21_2.PLAYER_INFO_UPDATE, wrapper -> {\n            final BitSet actions = wrapper.read(Types.PROFILE_ACTIONS_ENUM1_21_2);\n            // We need to recreate the BitSet field itself to remove the new action\n            final BitSet updatedActions = new BitSet(6);\n            for (int i = 0; i < 6; i++) {\n                if (actions.get(i)) {\n                    updatedActions.set(i);\n                }\n            }\n            wrapper.write(Types.PROFILE_ACTIONS_ENUM1_19_3, updatedActions);\n\n            final int entries = wrapper.passthrough(Types.VAR_INT);\n            for (int i = 0; i < entries; i++) {\n                wrapper.passthrough(Types.UUID);\n                if (actions.get(0)) {\n                    wrapper.passthrough(Types.STRING); // Player Name\n                    wrapper.passthrough(Types.PROFILE_PROPERTY_ARRAY);\n                }\n                if (actions.get(1) && wrapper.passthrough(Types.BOOLEAN)) {\n                    wrapper.passthrough(Types.UUID); // Session UUID\n                    wrapper.passthrough(Types.PROFILE_KEY);\n                }\n                if (actions.get(2)) {\n                    wrapper.passthrough(Types.VAR_INT); // Gamemode\n                }\n                if (actions.get(3)) {\n                    wrapper.passthrough(Types.BOOLEAN); // Listed\n                }\n                if (actions.get(4)) {\n                    wrapper.passthrough(Types.VAR_INT); // Latency\n                }\n                if (actions.get(5)) {\n                    final Tag displayName = wrapper.passthrough(Types.TRUSTED_OPTIONAL_TAG);\n                    protocol.getComponentRewriter().processTag(wrapper.user(), displayName);\n                }\n\n                // New one\n                if (actions.get(6)) {\n                    wrapper.read(Types.VAR_INT); // List order\n                }\n            }\n        });\n        protocol.registerClientbound(ClientboundPackets1_21_2.SET_PASSENGERS, wrapper -> {\n            final int vehicleId = wrapper.passthrough(Types.VAR_INT);\n            final int[] passengerIds = wrapper.passthrough(Types.VAR_INT_ARRAY_PRIMITIVE);\n            final ClientVehicleStorage storage = wrapper.user().get(ClientVehicleStorage.class);\n            if (storage != null && vehicleId == storage.vehicleId()) {\n                wrapper.user().remove(ClientVehicleStorage.class);\n                sendSneakingPlayerCommand(wrapper, false);\n            }\n\n            final int clientEntityId = tracker(wrapper.user()).clientEntityId();\n            for (final int passenger : passengerIds) {\n                if (passenger == clientEntityId) {\n                    wrapper.user().put(new ClientVehicleStorage(vehicleId));\n                    break;\n                }\n            }\n        });\n        protocol.appendClientbound(ClientboundPackets1_21_2.REMOVE_ENTITIES, wrapper -> {\n            final ClientVehicleStorage vehicleStorage = wrapper.user().get(ClientVehicleStorage.class);\n            if (vehicleStorage == null) {\n                return;\n            }\n\n            final int[] entityIds = wrapper.get(Types.VAR_INT_ARRAY_PRIMITIVE, 0);\n            for (final int entityId : entityIds) {\n                if (entityId == vehicleStorage.vehicleId()) {\n                    wrapper.user().remove(ClientVehicleStorage.class);\n                    sendSneakingPlayerCommand(wrapper, false);\n                    break;\n                }\n            }\n        });\n    }\n\n    private void updateParticleFormat(final CompoundTag options, final String particleType) {\n        // Have to be float lists in older versions\n        if (\"dust_color_transition\".equals(particleType)) {\n            replaceColor(options, \"from_color\");\n            replaceColor(options, \"to_color\");\n        } else if (\"dust\".equals(particleType)) {\n            replaceColor(options, \"color\");\n        }\n    }\n\n    private void replaceColor(final CompoundTag options, final String to_color) {\n        final IntTag toColorTag = options.getIntTag(to_color);\n        if (toColorTag == null) {\n            return;\n        }\n\n        final int rgb = toColorTag.asInt();\n        final float r = ((rgb >> 16) & 0xFF) / 255F;\n        final float g = ((rgb >> 8) & 0xFF) / 255F;\n        final float b = (rgb & 0xFF) / 255F;\n        options.put(to_color, new ListTag<>(List.of(new FloatTag(r), new FloatTag(g), new FloatTag(b))));\n    }\n\n    private void writePackedRotation(final PacketWrapper wrapper, final float yaw, final float pitch) {\n        // Pack y and x rot\n        wrapper.write(Types.BYTE, (byte) Math.floor(yaw * 256F / 360F));\n        wrapper.write(Types.BYTE, (byte) Math.floor(pitch * 256F / 360F));\n    }\n\n    private void handleRelativeArguments(\n        final PacketWrapper wrapper,\n        double x, double y, double z,\n        float yaw, float pitch,\n        final int relativeArguments,\n        double movementX, double movementY, double movementZ,\n        @Nullable final Integer entityId\n    ) {\n        // Position and rotation\n        final PlayerStorage storage = wrapper.user().get(PlayerStorage.class);\n        if ((relativeArguments & 1 << REL_X) != 0) {\n            x += storage.x();\n        }\n        if ((relativeArguments & 1 << REL_Y) != 0) {\n            y += storage.y();\n        }\n        if ((relativeArguments & 1 << REL_Z) != 0) {\n            z += storage.z();\n        }\n        if ((relativeArguments & 1 << REL_Y_ROT) != 0) {\n            yaw += storage.yaw();\n        }\n        if ((relativeArguments & 1 << REL_X_ROT) != 0) {\n            pitch += storage.pitch();\n        }\n\n        // Movement rotation\n        if ((relativeArguments & 1 << REL_ROTATE_DELTA) != 0) {\n            final double deltaYaw = Math.toRadians(storage.yaw() - yaw);\n            final double deltaYawCos = Math.cos(deltaYaw);\n            final double deltaYawSin = Math.sin(deltaYaw);\n            movementX = movementX * deltaYawCos + movementZ * deltaYawSin;\n            movementZ = movementZ * deltaYawCos - movementX * deltaYawSin;\n\n            final double deltaPitch = Math.toRadians(storage.pitch() - pitch);\n            final double deltaPitchCos = Math.cos(deltaPitch);\n            final double deltaPitchSin = Math.sin(deltaPitch);\n            movementY = movementY * deltaPitchCos + movementZ * deltaPitchSin;\n            movementZ = movementZ * deltaPitchCos - movementY * deltaPitchSin;\n        }\n\n        final boolean relativeDeltaX = (relativeArguments & 1 << REL_DELTA_X) != 0;\n        final boolean relativeDeltaY = (relativeArguments & 1 << REL_DELTA_Y) != 0;\n        final boolean relativeDeltaZ = (relativeArguments & 1 << REL_DELTA_Z) != 0;\n\n        // Update after having used its previous data\n        storage.setPosition(x, y, z);\n        storage.setRotation(yaw, pitch);\n\n        // Movement\n        if (relativeDeltaX && relativeDeltaY && relativeDeltaZ) {\n            if (entityId != null && entityId != tracker(wrapper.user()).clientEntityId()) {\n                // Can only handle the client entity\n                return;\n            }\n\n            final PacketWrapper explosionPacket = wrapper.create(ClientboundPackets1_21.EXPLODE);\n            explosionPacket.write(Types.DOUBLE, 0.0); // Center X\n            explosionPacket.write(Types.DOUBLE, 0.0); // Center Y\n            explosionPacket.write(Types.DOUBLE, 0.0); // Center Z\n            explosionPacket.write(Types.FLOAT, 0F); // Power\n            explosionPacket.write(Types.VAR_INT, 0); // Blocks affected\n            explosionPacket.write(Types.FLOAT, (float) movementX);\n            explosionPacket.write(Types.FLOAT, (float) movementY);\n            explosionPacket.write(Types.FLOAT, (float) movementZ);\n            explosionPacket.write(Types.VAR_INT, 0); // Block interaction\n            explosionPacket.write(VersionedTypes.V1_21.particle(), new Particle(0)); // Small explosion\n            explosionPacket.write(VersionedTypes.V1_21.particle(), new Particle(0)); // Large explosion\n            explosionPacket.write(Types.SOUND_EVENT, Holder.of(new SoundEvent(\"\", null))); // Explosion sound\n\n            explosionPacket.send(Protocol1_21_2To1_21.class);\n        } else if (!relativeDeltaX && !relativeDeltaY && !relativeDeltaZ) {\n            final PacketWrapper entityMotionPacket = wrapper.create(ClientboundPackets1_21.SET_ENTITY_MOTION);\n            entityMotionPacket.write(Types.VAR_INT, entityId != null ? entityId : tracker(wrapper.user()).clientEntityId());\n            entityMotionPacket.write(Types.SHORT, VelocityUtil.toLegacyVelocity(movementX));\n            entityMotionPacket.write(Types.SHORT, VelocityUtil.toLegacyVelocity(movementY));\n            entityMotionPacket.write(Types.SHORT, VelocityUtil.toLegacyVelocity(movementZ));\n\n            entityMotionPacket.send(Protocol1_21_2To1_21.class);\n        } else if (!warned) {\n            // Mixed combinations of relative and absolute would require tracking the previous delta movement\n            // which is quite impossible without doing massive player simulation on protocol level.\n\n            // This is bad but so is life.\n            protocol.getLogger().warning(\"Mixed combinations of relative and absolute delta movements are not supported for 1.21.1 players. \" +\n                \"This will result in incorrect movement for the player. \");\n            warned = true;\n        }\n    }\n\n    private void sendSneakingPlayerCommand(final PacketWrapper wrapper, final boolean sneaking) {\n        final PlayerStorage sneakingStorage = wrapper.user().get(PlayerStorage.class);\n        if (sneakingStorage.setSneaking(sneaking)) {\n            final PacketWrapper playerCommandPacket = wrapper.create(ServerboundPackets1_21_2.PLAYER_COMMAND);\n            playerCommandPacket.write(Types.VAR_INT, tracker(wrapper.user()).clientEntityId());\n            playerCommandPacket.write(Types.VAR_INT, sneaking ? 0 : 1); // Start/stop sneaking\n            playerCommandPacket.write(Types.VAR_INT, 0); // Data\n            playerCommandPacket.sendToServer(Protocol1_21_2To1_21.class);\n        }\n    }\n\n    private int boatTypeFromEntityType(final EntityType type) {\n        if (type == EntityTypes1_21_2.OAK_BOAT) {\n            return 0;\n        } else if (type == EntityTypes1_21_2.SPRUCE_BOAT) {\n            return 1;\n        } else if (type == EntityTypes1_21_2.BIRCH_BOAT) {\n            return 2;\n        } else if (type == EntityTypes1_21_2.JUNGLE_BOAT) {\n            return 3;\n        } else if (type == EntityTypes1_21_2.ACACIA_BOAT) {\n            return 4;\n        } else if (type == EntityTypes1_21_2.CHERRY_BOAT) {\n            return 5;\n        } else if (type == EntityTypes1_21_2.DARK_OAK_BOAT) {\n            return 6;\n        } else if (type == EntityTypes1_21_2.MANGROVE_BOAT) {\n            return 7;\n        } else if (type == EntityTypes1_21_2.BAMBOO_RAFT) {\n            return 8;\n        } else {\n            return 0;\n        }\n    }\n\n    private int chestBoatTypeFromEntityType(final EntityType type) {\n        if (type == EntityTypes1_21_2.OAK_CHEST_BOAT) {\n            return 0;\n        } else if (type == EntityTypes1_21_2.SPRUCE_CHEST_BOAT) {\n            return 1;\n        } else if (type == EntityTypes1_21_2.BIRCH_CHEST_BOAT) {\n            return 2;\n        } else if (type == EntityTypes1_21_2.JUNGLE_CHEST_BOAT) {\n            return 3;\n        } else if (type == EntityTypes1_21_2.ACACIA_CHEST_BOAT) {\n            return 4;\n        } else if (type == EntityTypes1_21_2.CHERRY_CHEST_BOAT) {\n            return 5;\n        } else if (type == EntityTypes1_21_2.DARK_OAK_CHEST_BOAT) {\n            return 6;\n        } else if (type == EntityTypes1_21_2.MANGROVE_CHEST_BOAT) {\n            return 7;\n        } else if (type == EntityTypes1_21_2.BAMBOO_CHEST_RAFT) {\n            return 8;\n        } else {\n            return 0;\n        }\n    }\n\n    private void fixOnGround(final PacketWrapper wrapper) {\n        final boolean data = wrapper.read(Types.BOOLEAN);\n        wrapper.write(Types.UNSIGNED_BYTE, data ? (short) 1 : 0); // Carries more data now\n    }\n\n    @Override\n    protected void registerRewrites() {\n        dataTypeMapper().register();\n        registerEntityDataTypeHandler1_20_3(\n            VersionedTypes.V1_21.entityDataTypes.itemType,\n            VersionedTypes.V1_21.entityDataTypes.blockStateType,\n            VersionedTypes.V1_21.entityDataTypes.optionalBlockStateType,\n            VersionedTypes.V1_21.entityDataTypes.particleType,\n            VersionedTypes.V1_21.entityDataTypes.particlesType,\n            VersionedTypes.V1_21.entityDataTypes.componentType,\n            VersionedTypes.V1_21.entityDataTypes.optionalComponentType\n        );\n        registerBlockStateHandler(EntityTypes1_21_2.ABSTRACT_MINECART, 11);\n\n        filter().type(EntityTypes1_21_2.CREAKING).cancel(17); // Active\n        filter().type(EntityTypes1_21_2.CREAKING).cancel(16); // Can move\n\n        filter().type(EntityTypes1_21_2.ABSTRACT_BOAT).addIndex(11); // Boat type\n        filter().type(EntityTypes1_21_2.SALMON).removeIndex(17); // Data type\n        filter().type(EntityTypes1_21_2.AGEABLE_WATER_CREATURE).removeIndex(16); // Baby\n\n        filter().type(EntityTypes1_21_2.ABSTRACT_ARROW).removeIndex(10); // In ground\n    }\n\n    @Override\n    public EntityType typeFromId(final int type) {\n        return EntityTypes1_21_2.getTypeFromId(type);\n    }\n\n    @Override\n    public void onMappingDataLoaded() {\n        super.onMappingDataLoaded();\n        mapEntityTypeWithData(EntityTypes1_21_2.CREAKING, EntityTypes1_21_2.WARDEN).tagName();\n        mapEntityTypeWithData(EntityTypes1_21_2.CREAKING_TRANSIENT, EntityTypes1_21_2.WARDEN).tagName();\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_2to1_21/rewriter/ParticleRewriter1_21_2.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_2to1_21.rewriter;\n\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.Particle;\nimport com.viaversion.viaversion.api.protocol.Protocol;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ClientboundPacket1_21_2;\nimport com.viaversion.viaversion.rewriter.ParticleRewriter;\n\npublic final class ParticleRewriter1_21_2 extends ParticleRewriter<ClientboundPacket1_21_2> {\n\n    public ParticleRewriter1_21_2(final Protocol<ClientboundPacket1_21_2, ?, ?, ?> protocol) {\n        super(protocol);\n    }\n\n    @Override\n    public void rewriteParticle(final UserConnection connection, final Particle particle) {\n        final String identifier = protocol.getMappingData().getParticleMappings().identifier(particle.id());\n        super.rewriteParticle(connection, particle);\n\n        if (identifier.equals(\"minecraft:dust_color_transition\")) {\n            argbToVector(particle, 0);\n            argbToVector(particle, 3);\n        } else if (identifier.equals(\"minecraft:dust\")) {\n            argbToVector(particle, 0);\n        } else if (identifier.equals(\"minecraft:trail\")) {\n            // Remove target\n            particle.removeArgument(2);\n            particle.removeArgument(1);\n            particle.removeArgument(0);\n        }\n    }\n\n    private void argbToVector(final Particle particle, final int index) {\n        final int argb = particle.<Integer>removeArgument(index).getValue();\n        final float r = ((argb >> 16) & 0xFF) / 255F;\n        final float g = ((argb >> 8) & 0xFF) / 255F;\n        final float b = (argb & 0xFF) / 255F;\n        particle.add(index, Types.FLOAT, r);\n        particle.add(index + 1, Types.FLOAT, g);\n        particle.add(index + 2, Types.FLOAT, b);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_2to1_21/storage/InventoryStateIdStorage.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_2to1_21.storage;\n\nimport com.viaversion.viaversion.api.connection.StorableObject;\n\npublic final class InventoryStateIdStorage implements StorableObject {\n\n    private boolean smithingTableOpen;\n    private int stateId = -1;\n\n    public int stateId() {\n        return stateId;\n    }\n\n    public void setStateId(final int stateId) {\n        this.stateId = stateId;\n    }\n\n    public boolean smithingTableOpen() {\n        return smithingTableOpen;\n    }\n\n    public void setSmithingTableOpen(final boolean smithingTableOpen) {\n        this.smithingTableOpen = smithingTableOpen;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_2to1_21/storage/ItemTagStorage.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_2to1_21.storage;\n\nimport com.viaversion.viaversion.api.connection.StorableObject;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.util.Key;\nimport java.util.HashMap;\nimport java.util.Map;\nimport org.checkerframework.checker.nullness.qual.Nullable;\n\npublic final class ItemTagStorage implements StorableObject {\n\n    private Map<String, int[]> itemTags = new HashMap<>();\n\n    public int @Nullable [] itemTag(final String key) {\n        return itemTags.get(Key.stripMinecraftNamespace(key));\n    }\n\n    public void readItemTags(final PacketWrapper wrapper) {\n        final int length = wrapper.passthrough(Types.VAR_INT);\n        for (int i = 0; i < length; i++) {\n            final String registryKey = wrapper.passthrough(Types.STRING);\n            final int tagsSize = wrapper.passthrough(Types.VAR_INT);\n\n            final boolean itemRegistry = Key.stripMinecraftNamespace(registryKey).equals(\"item\");\n            if (itemRegistry) {\n                this.itemTags = new HashMap<>(tagsSize);\n            }\n\n            for (int j = 0; j < tagsSize; j++) {\n                final String key = wrapper.passthrough(Types.STRING);\n                final int[] ids = wrapper.passthrough(Types.VAR_INT_ARRAY_PRIMITIVE);\n                if (itemRegistry) {\n                    this.itemTags.put(Key.stripMinecraftNamespace(key), ids);\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_2to1_21/storage/PlayerStorage.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_2to1_21.storage;\n\nimport com.viaversion.viabackwards.api.entities.storage.PlayerPositionStorage;\nimport com.viaversion.viabackwards.protocol.v1_21_2to1_21.Protocol1_21_2To1_21;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ServerboundPackets1_21_2;\n\npublic final class PlayerStorage extends PlayerPositionStorage {\n    private static final PlayerInput EMPTY = new PlayerInput(false, false, false, false, false, false, false);\n    private static final float PLAYER_JUMP_HEIGHT = 0.42F;\n\n    private float yaw;\n    private float pitch;\n\n    private boolean playerCommandTrackedSneaking;\n    private boolean playerCommandTrackedSprinting;\n\n    private PlayerInput lastInput = EMPTY;\n    private double prevX;\n    private double prevY;\n    private double prevZ;\n\n    public void tick(final UserConnection user) {\n        final double deltaX = x() - prevX;\n        final double deltaY = y() - prevY;\n        final double deltaZ = z() - prevZ;\n\n        final double magnitude = Math.sqrt(deltaX * deltaX + deltaZ * deltaZ);\n        double directionX = magnitude > 0 ? deltaX / magnitude : 0;\n        double directionZ = magnitude > 0 ? deltaZ / magnitude : 0;\n\n        directionX = Math.max(-1, Math.min(1, directionX));\n        directionZ = Math.max(-1, Math.min(1, directionZ));\n\n        final double angle = Math.toRadians(-yaw);\n        final double newDirectionX = directionX * Math.cos(angle) - directionZ * Math.sin(angle);\n        final double newDirectionZ = directionX * Math.sin(angle) + directionZ * Math.cos(angle);\n\n        final boolean forward = newDirectionZ >= 0.65F;\n        final boolean backwards = newDirectionZ <= -0.65F;\n        final boolean left = newDirectionX >= 0.65F;\n        final boolean right = newDirectionX <= -0.65F;\n        final boolean jump = Math.abs(deltaY - PLAYER_JUMP_HEIGHT) <= 1E-4F;\n\n        final PlayerInput input = new PlayerInput(forward, backwards, left, right, jump, playerCommandTrackedSneaking, playerCommandTrackedSprinting);\n        if (!lastInput.equals(input)) {\n            final PacketWrapper playerInputPacket = PacketWrapper.create(ServerboundPackets1_21_2.PLAYER_INPUT, user);\n            byte flags = 0;\n            flags = (byte) (flags | (input.forward() ? 1 : 0));\n            flags = (byte) (flags | (input.backward() ? 2 : 0));\n            flags = (byte) (flags | (input.left() ? 4 : 0));\n            flags = (byte) (flags | (input.right() ? 8 : 0));\n            flags = (byte) (flags | (input.jump() ? 16 : 0));\n            flags = (byte) (flags | (input.sneak() ? 32 : 0));\n            flags = (byte) (flags | (input.sprint() ? 64 : 0));\n            playerInputPacket.write(Types.BYTE, flags);\n\n            playerInputPacket.sendToServer(Protocol1_21_2To1_21.class);\n            lastInput = input;\n        }\n\n        this.prevX = x();\n        this.prevY = y();\n        this.prevZ = z();\n    }\n\n    public float yaw() {\n        return yaw;\n    }\n\n    public float pitch() {\n        return pitch;\n    }\n\n    public void setRotation(final float yaw, final float pitch) {\n        this.yaw = yaw;\n        this.pitch = pitch;\n    }\n\n    public void setPlayerCommandTrackedSneaking(final boolean playerCommandTrackedSneaking) {\n        this.playerCommandTrackedSneaking = playerCommandTrackedSneaking;\n    }\n\n    public void setPlayerCommandTrackedSprinting(final boolean playerCommandTrackedSprinting) {\n        this.playerCommandTrackedSprinting = playerCommandTrackedSprinting;\n    }\n\n    public boolean setSneaking(final boolean sneaking) {\n        final boolean changed = this.playerCommandTrackedSneaking != sneaking;\n        this.playerCommandTrackedSneaking = sneaking;\n        return changed;\n    }\n\n    public record PlayerInput(boolean forward, boolean backward, boolean left, boolean right, boolean jump,\n                              boolean sneak, boolean sprint) {\n    }\n\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_2to1_21/storage/RecipeStorage.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_2to1_21.storage;\n\nimport com.google.common.base.Preconditions;\nimport com.viaversion.viabackwards.protocol.v1_21_2to1_21.Protocol1_21_2To1_21;\nimport com.viaversion.viaversion.api.connection.StorableObject;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.HolderSet;\nimport com.viaversion.viaversion.api.minecraft.item.Item;\nimport com.viaversion.viaversion.api.minecraft.item.StructuredItem;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.version.VersionedTypes;\nimport com.viaversion.viaversion.protocols.v1_20_5to1_21.packet.ClientboundPackets1_21;\nimport java.util.ArrayList;\nimport java.util.Comparator;\nimport java.util.List;\n\n// Mostly a lost cause as the server will not send all the necessary data.\n// Recipe displays can also be different from the actual recipe - at the end of the same,\n// the server will still properly handle inputs, but we can't fully reconstruct the recipe book.\npublic final class RecipeStorage implements StorableObject {\n\n    // Pairs of open + filtering for: Crafting, furnace, blast furnace, smoker\n    public static final int RECIPE_BOOK_SETTINGS = 4 * 2;\n    private static final String[] EMPTY_STRINGS = new String[0];\n    private final List<Recipe> recipes = new ArrayList<>(); // Recipes, excluding stone cutter recipes, in the order they were received\n    private final List<Recipe> tempRecipes = new ArrayList<>(); // Recipes received in the current batch\n    private final List<StoneCutterRecipe> stoneCutterRecipes = new ArrayList<>();\n    private boolean[] recipeBookSettings = new boolean[RECIPE_BOOK_SETTINGS];\n    private final Protocol1_21_2To1_21 protocol;\n\n    public RecipeStorage(final Protocol1_21_2To1_21 protocol) {\n        this.protocol = protocol;\n    }\n\n    abstract static class Recipe {\n        private static final int FOOD_CRAFTING_BOOK_CATEGORY = 0;\n        private static final int BLOCKS_CRAFTING_BOOK_CATEGORY = 1;\n        private static final int MISC_CRAFTING_BOOK_CATEGORY = 2;\n        protected int index;\n        private Integer group;\n        private int category;\n        private boolean highlight;\n\n        abstract void write(PacketWrapper wrapper);\n\n        void writeGroup(final PacketWrapper wrapper) {\n            wrapper.write(Types.STRING, group != null ? Integer.toString(group) : \"\");\n        }\n\n        void writeIngredients(final PacketWrapper wrapper, final Item[][] ingredients) {\n            wrapper.write(Types.VAR_INT, ingredients.length);\n            for (final Item[] ingredient : ingredients) {\n                writeIngredient(wrapper, ingredient);\n            }\n        }\n\n        void writeIngredient(final PacketWrapper wrapper, final Item[] ingredient) {\n            final Item[] copy = new Item[ingredient.length];\n            for (int i = 0; i < ingredient.length; i++) {\n                copy[i] = ingredient[i].copy();\n            }\n            wrapper.write(VersionedTypes.V1_21_2.itemArray(), copy);\n        }\n\n        void writeResult(final PacketWrapper wrapper, final Item result) {\n            wrapper.write(VersionedTypes.V1_21_2.item(), result.copy());\n        }\n\n        void writeCategory(final PacketWrapper wrapper) {\n            // TODO Doesn't translate exactly\n            final int craftingBookCategory = switch (category) {\n                case 4, 9, 12 -> FOOD_CRAFTING_BOOK_CATEGORY;\n                case 0, 5, 7, 10 -> BLOCKS_CRAFTING_BOOK_CATEGORY;\n                case 1, 2, 3, 6, 8, 11 -> MISC_CRAFTING_BOOK_CATEGORY;\n                default -> MISC_CRAFTING_BOOK_CATEGORY;\n            };\n            wrapper.write(Types.VAR_INT, craftingBookCategory);\n        }\n\n        int category() {\n            return category;\n        }\n    }\n\n    public void sendRecipes(final UserConnection connection) {\n        // Fill from temp recipes so we can clear before if needed\n        if (!tempRecipes.isEmpty()) {\n            this.recipes.addAll(tempRecipes);\n            tempRecipes.clear();\n        }\n\n        int highestIndex = -1;\n        for (final Recipe recipe : recipes) {\n            highestIndex = Math.max(highestIndex, recipe.index);\n        }\n\n        // Add stonecutter recipes from update_recipes\n        final List<Recipe> recipes = new ArrayList<>(this.recipes);\n        for (final StoneCutterRecipe recipe : stoneCutterRecipes) {\n            recipe.index = ++highestIndex;\n            recipes.add(recipe);\n        }\n\n        // Sort by id\n        recipes.sort(Comparator.comparingInt(a -> a.index));\n\n        // Since the server only sends unlocked recipes, we need to re-send all recipes in UPDATE_RECIPES\n        final PacketWrapper updateRecipesPacket = PacketWrapper.create(ClientboundPackets1_21.UPDATE_RECIPES, connection);\n        updateRecipesPacket.write(Types.VAR_INT, recipes.size());\n        for (final Recipe recipe : recipes) {\n            updateRecipesPacket.write(Types.STRING, identifier(recipe.index));\n            recipe.write(updateRecipesPacket);\n        }\n        updateRecipesPacket.send(Protocol1_21_2To1_21.class);\n\n        sendUnlockedRecipes(connection, recipes);\n    }\n\n    private static String identifier(final int recipeIndex) {\n        // Use index as the recipe identifier, add leading zeros to keept it sorted\n        return String.format(\"%06d\", recipeIndex);\n    }\n\n    public void lockRecipes(final PacketWrapper wrapper, final int[] ids) {\n        for (final int id : ids) {\n            recipes.removeIf(recipe -> recipe.index == id);\n        }\n\n        wrapper.write(Types.VAR_INT, 2); // Remove recipes\n        for (final boolean recipeBookSetting : recipeBookSettings) {\n            wrapper.write(Types.BOOLEAN, recipeBookSetting);\n        }\n\n        final String[] recipeKeys = new String[ids.length];\n        for (int i = 0; i < ids.length; i++) {\n            recipeKeys[i] = identifier(ids[i]);\n        }\n        wrapper.write(Types.STRING_ARRAY, recipeKeys);\n    }\n\n    private void sendUnlockedRecipes(final UserConnection connection, final List<Recipe> recipes) {\n        final PacketWrapper wrapper = PacketWrapper.create(ClientboundPackets1_21.RECIPE, connection);\n        wrapper.write(Types.VAR_INT, 0); // Init recipes\n\n        for (final boolean recipeBookSetting : recipeBookSettings) {\n            wrapper.write(Types.BOOLEAN, recipeBookSetting);\n        }\n\n        // Use index as the recipe identifier. We only know the unlocked ones, so send all\n        final String[] recipeKeys = new String[recipes.size()];\n        final List<String> highlightRecipes = new ArrayList<>();\n        for (int i = 0; i < recipes.size(); i++) {\n            recipeKeys[i] = identifier(recipes.get(i).index);\n            if (recipes.get(i).highlight) {\n                highlightRecipes.add(recipeKeys[i]);\n            }\n        }\n        wrapper.write(Types.STRING_ARRAY, recipeKeys);\n\n        wrapper.write(Types.STRING_ARRAY, highlightRecipes.toArray(EMPTY_STRINGS));\n        wrapper.send(Protocol1_21_2To1_21.class);\n    }\n\n    public void readRecipe(final PacketWrapper wrapper) {\n        final int id = wrapper.read(Types.VAR_INT);\n        final int type = wrapper.passthrough(Types.VAR_INT);\n        final Recipe recipe = switch (type) {\n            case 0 -> readShapeless(wrapper);\n            case 1 -> readShaped(wrapper);\n            case 2 -> readFurnace(wrapper);\n            case 3 -> readStoneCutter(wrapper);\n            case 4 -> readSmithing(wrapper);\n            default -> null;\n        };\n\n        final Integer group = wrapper.read(Types.OPTIONAL_VAR_INT);\n        final int category = wrapper.read(Types.VAR_INT);\n        if (wrapper.read(Types.BOOLEAN)) {\n            final int ingredientsSize = wrapper.read(Types.VAR_INT);\n            for (int j = 0; j < ingredientsSize; j++) {\n                //handleIngredient(wrapper); // Items //TODO ?\n                wrapper.read(Types.HOLDER_SET);\n            }\n        }\n        final byte flags = wrapper.read(Types.BYTE);\n\n        if (recipe != null) {\n            recipe.index = id;\n            recipe.group = group;\n            recipe.category = category;\n            recipe.highlight = (flags & 2) != 0;\n        }\n    }\n\n    private Recipe readShapeless(final PacketWrapper wrapper) {\n        final Item[][] ingredients = readSlotDisplayList(wrapper);\n        final Item result = readSingleSlotDisplay(wrapper);\n        readSlotDisplay(wrapper); // Crafting station\n        return add(new ShapelessRecipe(ingredients, result));\n    }\n\n    private Recipe readShaped(final PacketWrapper wrapper) {\n        final int width = wrapper.passthrough(Types.VAR_INT);\n        final int height = wrapper.passthrough(Types.VAR_INT);\n        final Item[][] ingredients = readSlotDisplayList(wrapper);\n        final Item result = readSingleSlotDisplay(wrapper);\n        readSlotDisplay(wrapper); // Crafting station\n        return add(new ShapedRecipe(width, height, ingredients, result));\n    }\n\n    private Recipe readFurnace(final PacketWrapper wrapper) {\n        final Item[] ingredient = readSlotDisplay(wrapper);\n        readSlotDisplay(wrapper); // Fuel\n        final Item result = readSingleSlotDisplay(wrapper);\n        readSlotDisplay(wrapper); // Crafting station\n        final int duration = wrapper.read(Types.VAR_INT);\n        final float experience = wrapper.read(Types.FLOAT);\n        return add(new FurnaceRecipe(ingredient, result, duration, experience));\n    }\n\n    private Recipe readStoneCutter(final PacketWrapper wrapper) {\n        // Use values from UPDATE_RECIPES instead\n        readSlotDisplay(wrapper); // Input\n        readSlotDisplay(wrapper); // Result\n        readSlotDisplay(wrapper); // Crafting station\n        return null;\n    }\n\n    private Recipe readSmithing(final PacketWrapper wrapper) {\n        // TODO Combine with update_recipes?\n        readSlotDisplay(wrapper); // Template\n        readSlotDisplay(wrapper); // Base\n        readSlotDisplay(wrapper); // Addition\n        readSlotDisplay(wrapper); // Result\n        readSlotDisplay(wrapper); // Crafting station\n        return null;\n    }\n\n    private Recipe add(final Recipe recipe) {\n        tempRecipes.add(recipe);\n        return recipe;\n    }\n\n    private Item[][] readSlotDisplayList(final PacketWrapper wrapper) {\n        final int size = wrapper.passthrough(Types.VAR_INT);\n        final Item[][] ingredients = new Item[size][];\n        for (int i = 0; i < size; i++) {\n            ingredients[i] = readSlotDisplay(wrapper);\n        }\n        return ingredients;\n    }\n\n    private Item readSingleSlotDisplay(final PacketWrapper wrapper) {\n        final Item[] items = readSlotDisplay(wrapper);\n        return items.length == 0 ? new StructuredItem(1, 1) : items[0];\n    }\n\n    private Item[] readSlotDisplay(final PacketWrapper wrapper) {\n        // empty, any_fuel, smithing_trim are empty\n        final int type = wrapper.read(Types.VAR_INT);\n        return switch (type) {\n            case 2 -> {\n                final int id = wrapper.read(Types.VAR_INT);\n                if (id == 0) {\n                    protocol.getLogger().warning(\"Empty item id in recipe\");\n                    yield new Item[0];\n                }\n                yield new Item[]{new StructuredItem(rewriteItemId(id), 1)};\n            }\n            case 3 -> {\n                final Item item = protocol.getItemRewriter().handleItemToClient(wrapper.user(), wrapper.read(VersionedTypes.V1_21_2.item()));\n                if (item.isEmpty()) {\n                    protocol.getLogger().warning(\"Empty item in recipe\");\n                    yield new Item[0];\n                }\n                yield new Item[]{item};\n            }\n            case 4 -> {\n                wrapper.read(Types.STRING); // Tag key // TODO Probably not even worth the effort\n                yield new Item[0];\n            }\n            case 5 -> {\n                readSlotDisplay(wrapper); // Base\n                readSlotDisplay(wrapper); // Material\n                readSlotDisplay(wrapper); // Pattern\n                yield new Item[0];\n            }\n            case 6 -> {\n                readSlotDisplay(wrapper); // Input\n                readSlotDisplay(wrapper); // Remainder\n                yield new Item[0];\n            }\n            case 7 -> readSlotDisplayList(wrapper)[0]; // Composite\n            default -> new Item[0];\n        };\n    }\n\n    private int rewriteItemId(final int id) {\n        return protocol.getMappingData().getNewItemId(id);\n    }\n\n    public void readStoneCutterRecipes(final PacketWrapper wrapper) {\n        stoneCutterRecipes.clear();\n        final int stonecutterRecipesSize = wrapper.read(Types.VAR_INT);\n        for (int i = 0; i < stonecutterRecipesSize; i++) {\n            // The ingredients are what's actually used in client prediction, they're the important part\n            final Item[] ingredient = readHolderSet(wrapper);\n            // TODO Probably not actually the result, might have to combine with update_recipes\n            final Item result = readSingleSlotDisplay(wrapper);\n            stoneCutterRecipes.add(new StoneCutterRecipe(ingredient, result));\n        }\n    }\n\n    private Item[] readHolderSet(final PacketWrapper wrapper) {\n        final HolderSet holderSet = wrapper.read(Types.HOLDER_SET);\n        if (holderSet.hasTagKey()) {\n            return new Item[]{new StructuredItem(1, 1)}; // TODO Probably not even worth the effort\n        }\n\n        final int[] ids = holderSet.ids();\n        for (int i = 0; i < ids.length; i++) {\n            ids[i] = rewriteItemId(ids[i]);\n        }\n\n        final Item[] ingredient = new Item[ids.length];\n        for (int i = 0; i < ingredient.length; i++) {\n            ingredient[i] = new StructuredItem(ids[i], 1);\n        }\n        return ingredient;\n    }\n\n    private static final class ShapelessRecipe extends Recipe {\n        private static final int SERIALIZER_ID = 1;\n        private final Item[][] ingredients;\n        private final Item result;\n\n        private ShapelessRecipe(final Item[][] ingredients, final Item result) {\n            this.ingredients = ingredients;\n            this.result = result;\n        }\n\n        @Override\n        public void write(final PacketWrapper wrapper) {\n            wrapper.write(Types.VAR_INT, SERIALIZER_ID);\n            writeGroup(wrapper);\n            writeCategory(wrapper);\n            writeIngredients(wrapper, ingredients);\n            writeResult(wrapper, result);\n        }\n    }\n\n    private static final class ShapedRecipe extends Recipe {\n        private static final int SERIALIZER_ID = 0;\n        private final int width;\n        private final int height;\n        private final Item[][] ingredients;\n        private final Item result;\n\n        private ShapedRecipe(final int width, final int height, final Item[][] ingredients, final Item result) {\n            this.width = width;\n            this.height = height;\n            this.ingredients = ingredients;\n            this.result = result;\n        }\n\n        @Override\n        public void write(final PacketWrapper wrapper) {\n            wrapper.write(Types.VAR_INT, SERIALIZER_ID);\n            writeGroup(wrapper);\n            writeCategory(wrapper);\n            wrapper.write(Types.VAR_INT, width);\n            wrapper.write(Types.VAR_INT, height);\n            Preconditions.checkArgument(width * height == ingredients.length, \"Invalid shaped recipe\");\n            // No length prefix\n            for (final Item[] ingredient : ingredients) {\n                writeIngredient(wrapper, ingredient);\n            }\n            writeResult(wrapper, result);\n            wrapper.write(Types.BOOLEAN, false); // Doesn't matter for the init\n        }\n    }\n\n    private static final class FurnaceRecipe extends Recipe {\n        private final Item[] ingredient;\n        private final Item result;\n        private final float experience;\n        private final int cookingTime;\n\n        private FurnaceRecipe(final Item[] ingredient, final Item result, final int cookingTime, final float experience) {\n            this.ingredient = ingredient;\n            this.result = result;\n            this.experience = experience;\n            this.cookingTime = cookingTime;\n        }\n\n        @Override\n        public void write(final PacketWrapper wrapper) {\n            wrapper.write(Types.VAR_INT, serializerId());\n            writeGroup(wrapper);\n            writeCategory(wrapper);\n            writeIngredient(wrapper, ingredient);\n            writeResult(wrapper, result);\n            wrapper.write(Types.FLOAT, experience);\n            wrapper.write(Types.VAR_INT, cookingTime);\n        }\n\n        private int serializerId() {\n            return switch (category()) {\n                case 4, 5, 6 -> 15; // Furnace food, bocks, misc\n                case 7, 8 -> 16; // Blast furnace blocks, misc\n                case 9 -> 17; // Smoker\n                case 12 -> 18; // Campfire\n                default -> 15;\n            };\n        }\n    }\n\n    private static final class StoneCutterRecipe extends Recipe {\n        private static final int SERIALIZER_ID = 19;\n        private final Item[] ingredient;\n        private final Item result;\n\n        private StoneCutterRecipe(final Item[] ingredient, final Item result) {\n            this.ingredient = ingredient;\n            this.result = result;\n        }\n\n        @Override\n        public void write(final PacketWrapper wrapper) {\n            wrapper.write(Types.VAR_INT, SERIALIZER_ID);\n            writeGroup(wrapper);\n            writeIngredient(wrapper, ingredient);\n            writeResult(wrapper, result);\n        }\n    }\n\n    public void setRecipeBookSettings(final boolean[] recipeBookSettings) {\n        this.recipeBookSettings = recipeBookSettings;\n    }\n\n    public void clearRecipes() {\n        recipes.clear();\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_2to1_21/storage/SignStorage.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_2to1_21.storage;\n\nimport com.viaversion.viaversion.api.connection.StorableObject;\nimport com.viaversion.viaversion.api.minecraft.BlockPosition;\nimport java.util.HashSet;\nimport java.util.Set;\n\npublic final class SignStorage implements StorableObject {\n\n    private final Set<BlockPosition> signs = new HashSet<>();\n\n    public void addSign(final BlockPosition position) {\n        signs.add(position);\n    }\n\n    public boolean isSign(final BlockPosition position) {\n        return signs.contains(position);\n    }\n\n    public void removeSign(final BlockPosition position) {\n        signs.remove(position);\n    }\n\n    public void removeSigns(final int chunkX, final int chunkZ) {\n        signs.removeIf(pos -> pos.x() >> 4 == chunkX && pos.z() >> 4 == chunkZ);\n    }\n\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_2to1_21/task/PlayerPacketsTickTask.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_2to1_21.task;\n\nimport com.viaversion.viabackwards.ViaBackwards;\nimport com.viaversion.viabackwards.protocol.v1_21_2to1_21.Protocol1_21_2To1_21;\nimport com.viaversion.viabackwards.protocol.v1_21_2to1_21.storage.PlayerStorage;\nimport com.viaversion.viaversion.api.connection.ProtocolInfo;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.data.entity.EntityTracker;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.protocol.packet.State;\nimport com.viaversion.viaversion.connection.StorableObjectTask;\nimport com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ServerboundPackets1_21_2;\nimport com.viaversion.viaversion.protocols.v1_21to1_21_2.storage.ClientVehicleStorage;\nimport java.util.logging.Level;\n\npublic final class PlayerPacketsTickTask extends StorableObjectTask<PlayerStorage> {\n\n    public PlayerPacketsTickTask() {\n        super(PlayerStorage.class);\n    }\n\n    @Override\n    public void run(final UserConnection connection, final PlayerStorage storableObject) {\n        final ProtocolInfo protocolInfo = connection.getProtocolInfo();\n        if (protocolInfo.getClientState() != State.PLAY || protocolInfo.getServerState() != State.PLAY) {\n            return;\n        }\n\n        final EntityTracker entityTracker = connection.getEntityTracker(Protocol1_21_2To1_21.class);\n        if (!entityTracker.hasClientEntityId()) {\n            return;\n        }\n\n        try {\n            if (!connection.has(ClientVehicleStorage.class)) {\n                storableObject.tick(connection);\n            }\n        } catch (final Throwable t) {\n            ViaBackwards.getPlatform().getLogger().log(Level.SEVERE, \"Error while sending player input packet.\", t);\n        }\n\n        try {\n            final PacketWrapper clientTickEndPacket = PacketWrapper.create(ServerboundPackets1_21_2.CLIENT_TICK_END, connection);\n            clientTickEndPacket.sendToServer(Protocol1_21_2To1_21.class);\n        } catch (final Throwable t) {\n            ViaBackwards.getPlatform().getLogger().log(Level.SEVERE, \"Error while sending client tick end packet.\", t);\n        }\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_4to1_21_2/Protocol1_21_4To1_21_2.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_4to1_21_2;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.ListTag;\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viabackwards.api.data.BackwardsMappingData;\nimport com.viaversion.viabackwards.api.rewriters.BackwardsRegistryRewriter;\nimport com.viaversion.viabackwards.api.rewriters.text.JsonNBTComponentRewriter;\nimport com.viaversion.viabackwards.protocol.v1_21_4to1_21_2.rewriter.BlockItemPacketRewriter1_21_4;\nimport com.viaversion.viabackwards.protocol.v1_21_4to1_21_2.rewriter.ComponentRewriter1_21_4;\nimport com.viaversion.viabackwards.protocol.v1_21_4to1_21_2.rewriter.EntityPacketRewriter1_21_4;\nimport com.viaversion.viabackwards.protocol.v1_21_4to1_21_2.rewriter.ParticleRewriter1_21_4;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.Particle;\nimport com.viaversion.viaversion.api.minecraft.RegistryEntry;\nimport com.viaversion.viaversion.api.minecraft.RegistryType;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_21_4;\nimport com.viaversion.viaversion.api.protocol.packet.provider.PacketTypesProvider;\nimport com.viaversion.viaversion.api.protocol.packet.provider.SimplePacketTypesProvider;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.chunk.ChunkType1_20_2;\nimport com.viaversion.viaversion.api.type.types.version.VersionedTypes;\nimport com.viaversion.viaversion.api.type.types.version.VersionedTypesHolder;\nimport com.viaversion.viaversion.data.entity.EntityTrackerBase;\nimport com.viaversion.viaversion.protocols.v1_20_3to1_20_5.packet.ServerboundConfigurationPackets1_20_5;\nimport com.viaversion.viaversion.protocols.v1_20_5to1_21.packet.ClientboundConfigurationPackets1_21;\nimport com.viaversion.viaversion.protocols.v1_21_2to1_21_4.Protocol1_21_2To1_21_4;\nimport com.viaversion.viaversion.protocols.v1_21_2to1_21_4.packet.ServerboundPacket1_21_4;\nimport com.viaversion.viaversion.protocols.v1_21_2to1_21_4.packet.ServerboundPackets1_21_4;\nimport com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ClientboundPacket1_21_2;\nimport com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ClientboundPackets1_21_2;\nimport com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ServerboundPacket1_21_2;\nimport com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ServerboundPackets1_21_2;\nimport com.viaversion.viaversion.rewriter.BlockRewriter;\nimport com.viaversion.viaversion.rewriter.ParticleRewriter;\nimport com.viaversion.viaversion.rewriter.RecipeDisplayRewriter;\nimport com.viaversion.viaversion.rewriter.TagRewriter;\nimport com.viaversion.viaversion.util.ArrayUtil;\nimport com.viaversion.viaversion.util.Key;\nimport java.util.BitSet;\n\nimport static com.viaversion.viaversion.util.ProtocolUtil.packetTypeMap;\n\npublic final class Protocol1_21_4To1_21_2 extends BackwardsProtocol<ClientboundPacket1_21_2, ClientboundPacket1_21_2, ServerboundPacket1_21_4, ServerboundPacket1_21_2> {\n\n    public static final BackwardsMappingData MAPPINGS = new BackwardsMappingData(\"1.21.4\", \"1.21.2\", Protocol1_21_2To1_21_4.class);\n    private final EntityPacketRewriter1_21_4 entityRewriter = new EntityPacketRewriter1_21_4(this);\n    private final BlockItemPacketRewriter1_21_4 itemRewriter = new BlockItemPacketRewriter1_21_4(this);\n    private final ParticleRewriter<ClientboundPacket1_21_2> particleRewriter = new ParticleRewriter1_21_4(this);\n    private final JsonNBTComponentRewriter<ClientboundPacket1_21_2> translatableRewriter = new ComponentRewriter1_21_4(this);\n    private final TagRewriter<ClientboundPacket1_21_2> tagRewriter = new TagRewriter<>(this);\n    private final RecipeDisplayRewriter<ClientboundPacket1_21_2> recipeRewriter = new RecipeDisplayRewriter<>(this);\n    private final BlockRewriter<ClientboundPacket1_21_2> blockRewriter = BlockRewriter.for1_20_2(this, ChunkType1_20_2::new);\n    private final BackwardsRegistryRewriter registryDataRewriter = new BackwardsRegistryRewriter(this) {\n        @Override\n        public RegistryEntry[] handle(final UserConnection connection, final String key, final RegistryEntry[] entries) {\n            final String strippedKey = Key.stripMinecraftNamespace(key);\n            if (strippedKey.equals(\"worldgen/biome\")) {\n                for (final RegistryEntry entry : entries) {\n                    if (entry.tag() == null) {\n                        continue;\n                    }\n\n                    final CompoundTag effectsTag = ((CompoundTag) entry.tag()).getCompoundTag(\"effects\");\n                    final ListTag<CompoundTag> weightedMusicTags = effectsTag.getListTag(\"music\", CompoundTag.class);\n                    if (weightedMusicTags == null) {\n                        continue;\n                    }\n\n                    if (weightedMusicTags.isEmpty()) {\n                        effectsTag.remove(\"music\");\n                        continue;\n                    }\n\n                    // Unwrap music\n                    final CompoundTag musicTag = weightedMusicTags.get(0);\n                    effectsTag.put(\"music\", musicTag.get(\"data\"));\n                }\n            } else if (strippedKey.equals(\"trim_material\")) {\n                for (final RegistryEntry entry : entries) {\n                    if (entry.tag() == null) {\n                        continue;\n                    }\n\n                    final CompoundTag compoundTag = ((CompoundTag) entry.tag());\n                    compoundTag.putFloat(\"item_model_index\", itemModelIndex(entry.key()));\n                }\n            }\n\n            return super.handle(connection, key, entries);\n        }\n    };\n\n    public Protocol1_21_4To1_21_2() {\n        super(ClientboundPacket1_21_2.class, ClientboundPacket1_21_2.class, ServerboundPacket1_21_4.class, ServerboundPacket1_21_2.class);\n    }\n\n    @Override\n    protected void registerPackets() {\n        super.registerPackets();\n\n        replaceClientbound(ClientboundPackets1_21_2.LEVEL_PARTICLES, wrapper -> {\n            wrapper.passthrough(Types.BOOLEAN); // Override limiter\n            wrapper.read(Types.BOOLEAN); // Always show\n            wrapper.passthrough(Types.DOUBLE); // X\n            wrapper.passthrough(Types.DOUBLE); // Y\n            wrapper.passthrough(Types.DOUBLE); // Z\n            wrapper.passthrough(Types.FLOAT); // Offset X\n            wrapper.passthrough(Types.FLOAT); // Offset Y\n            wrapper.passthrough(Types.FLOAT); // Offset Z\n            wrapper.passthrough(Types.FLOAT); // Particle Data\n            wrapper.passthrough(Types.INT); // Particle Count\n            final Particle particle = wrapper.passthroughAndMap(VersionedTypes.V1_21_4.particle(), VersionedTypes.V1_21_2.particle());\n            particleRewriter.rewriteParticle(wrapper.user(), particle);\n        });\n\n        registerClientbound(ClientboundConfigurationPackets1_21.UPDATE_ENABLED_FEATURES, wrapper -> {\n            final String[] enabledFeatures = wrapper.read(Types.STRING_ARRAY);\n            wrapper.write(Types.STRING_ARRAY, ArrayUtil.add(enabledFeatures, \"winter_drop\"));\n        });\n\n        replaceClientbound(ClientboundPackets1_21_2.PLAYER_INFO_UPDATE, wrapper -> {\n            final BitSet actions = wrapper.read(Types.PROFILE_ACTIONS_ENUM1_21_4);\n            // Remove new action\n            final BitSet updatedActions = new BitSet(7);\n            for (int i = 0; i < 7; i++) {\n                if (actions.get(i)) {\n                    updatedActions.set(i);\n                }\n            }\n            wrapper.write(Types.PROFILE_ACTIONS_ENUM1_21_2, updatedActions);\n\n            final int entries = wrapper.passthrough(Types.VAR_INT);\n            for (int i = 0; i < entries; i++) {\n                wrapper.passthrough(Types.UUID);\n                if (actions.get(0)) {\n                    wrapper.passthrough(Types.STRING); // Player Name\n                    wrapper.passthrough(Types.PROFILE_PROPERTY_ARRAY);\n                }\n                if (actions.get(1) && wrapper.passthrough(Types.BOOLEAN)) {\n                    wrapper.passthrough(Types.UUID); // Session UUID\n                    wrapper.passthrough(Types.PROFILE_KEY);\n                }\n                if (actions.get(2)) {\n                    wrapper.passthrough(Types.VAR_INT); // Gamemode\n                }\n                if (actions.get(3)) {\n                    wrapper.passthrough(Types.BOOLEAN); // Listed\n                }\n                if (actions.get(4)) {\n                    wrapper.passthrough(Types.VAR_INT); // Latency\n                }\n                if (actions.get(5)) {\n                    translatableRewriter.processTag(wrapper.user(), wrapper.passthrough(Types.TRUSTED_OPTIONAL_TAG));\n                }\n                if (actions.get(6)) {\n                    wrapper.passthrough(Types.VAR_INT); // List order\n                }\n\n                // Remove\n                if (actions.get(7)) {\n                    wrapper.read(Types.BOOLEAN); // Show head\n                }\n            }\n        });\n    }\n\n    @Override\n    protected void onMappingDataLoaded() {\n        super.onMappingDataLoaded();\n        tagRewriter.addEmptyTags(RegistryType.ITEM, \"minecraft:tall_flowers\", \"minecraft:flowers\");\n        tagRewriter.addEmptyTag(RegistryType.BLOCK, \"minecraft:tall_flowers\");\n    }\n\n    @Override\n    public void init(final UserConnection user) {\n        addEntityTracker(user, new EntityTrackerBase(user, EntityTypes1_21_4.PLAYER));\n    }\n\n    @Override\n    public BackwardsMappingData getMappingData() {\n        return MAPPINGS;\n    }\n\n    @Override\n    public EntityPacketRewriter1_21_4 getEntityRewriter() {\n        return entityRewriter;\n    }\n\n    @Override\n    public BlockItemPacketRewriter1_21_4 getItemRewriter() {\n        return itemRewriter;\n    }\n\n    @Override\n    public BlockRewriter<ClientboundPacket1_21_2> getBlockRewriter() {\n        return blockRewriter;\n    }\n\n    @Override\n    public ParticleRewriter<ClientboundPacket1_21_2> getParticleRewriter() {\n        return particleRewriter;\n    }\n\n    @Override\n    public JsonNBTComponentRewriter<ClientboundPacket1_21_2> getComponentRewriter() {\n        return translatableRewriter;\n    }\n\n    @Override\n    public TagRewriter<ClientboundPacket1_21_2> getTagRewriter() {\n        return tagRewriter;\n    }\n\n    @Override\n    public RecipeDisplayRewriter<ClientboundPacket1_21_2> getRecipeRewriter() {\n        return recipeRewriter;\n    }\n\n    @Override\n    public BackwardsRegistryRewriter getRegistryDataRewriter() {\n        return registryDataRewriter;\n    }\n\n    @Override\n    public VersionedTypesHolder types() {\n        return VersionedTypes.V1_21_4;\n    }\n\n    @Override\n    public VersionedTypesHolder mappedTypes() {\n        return VersionedTypes.V1_21_2;\n    }\n\n    @Override\n    protected PacketTypesProvider<ClientboundPacket1_21_2, ClientboundPacket1_21_2, ServerboundPacket1_21_4, ServerboundPacket1_21_2> createPacketTypesProvider() {\n        return new SimplePacketTypesProvider<>(\n            packetTypeMap(unmappedClientboundPacketType, ClientboundPackets1_21_2.class, ClientboundConfigurationPackets1_21.class),\n            packetTypeMap(mappedClientboundPacketType, ClientboundPackets1_21_2.class, ClientboundConfigurationPackets1_21.class),\n            packetTypeMap(mappedServerboundPacketType, ServerboundPackets1_21_4.class, ServerboundConfigurationPackets1_20_5.class),\n            packetTypeMap(unmappedServerboundPacketType, ServerboundPackets1_21_2.class, ServerboundConfigurationPackets1_20_5.class)\n        );\n    }\n\n    private float itemModelIndex(final String trim) {\n        return switch (Key.stripNamespace(trim)) {\n            case \"amethyst\" -> 1.0F;\n            case \"copper\" -> 0.5F;\n            case \"diamond\" -> 0.8F;\n            case \"emerald\" -> 0.7F;\n            case \"gold\" -> 0.6F;\n            case \"iron\" -> 0.2F;\n            case \"lapis\" -> 0.9F;\n            case \"netherite\" -> 0.3F;\n            case \"quartz\" -> 0.1F;\n            case \"redstone\" -> 0.4F;\n            default -> 1.0f;\n        };\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_4to1_21_2/rewriter/BlockItemPacketRewriter1_21_4.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_4to1_21_2.rewriter;\n\nimport com.viaversion.nbt.tag.ByteArrayTag;\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.IntArrayTag;\nimport com.viaversion.nbt.tag.ListTag;\nimport com.viaversion.nbt.tag.StringTag;\nimport com.viaversion.viabackwards.ViaBackwards;\nimport com.viaversion.viabackwards.api.rewriters.BackwardsStructuredItemRewriter;\nimport com.viaversion.viabackwards.protocol.v1_21_4to1_21_2.Protocol1_21_4To1_21_2;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.data.StructuredDataContainer;\nimport com.viaversion.viaversion.api.minecraft.data.StructuredDataKey;\nimport com.viaversion.viaversion.api.minecraft.item.Item;\nimport com.viaversion.viaversion.api.minecraft.item.data.CustomModelData1_21_4;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ClientboundPacket1_21_2;\nimport com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ClientboundPackets1_21_2;\nimport com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ServerboundPacket1_21_2;\nimport com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ServerboundPackets1_21_2;\n\nimport static com.viaversion.viaversion.protocols.v1_21_2to1_21_4.rewriter.BlockItemPacketRewriter1_21_4.downgradeItemData;\nimport static com.viaversion.viaversion.protocols.v1_21_2to1_21_4.rewriter.BlockItemPacketRewriter1_21_4.updateItemData;\n\npublic final class BlockItemPacketRewriter1_21_4 extends BackwardsStructuredItemRewriter<ClientboundPacket1_21_2, ServerboundPacket1_21_2, Protocol1_21_4To1_21_2> {\n\n    public BlockItemPacketRewriter1_21_4(final Protocol1_21_4To1_21_2 protocol) {\n        super(protocol);\n    }\n\n    @Override\n    public void registerPackets() {\n        protocol.registerClientbound(ClientboundPackets1_21_2.SET_HELD_SLOT, wrapper -> {\n            final int slot = wrapper.read(Types.VAR_INT);\n            wrapper.write(Types.BYTE, (byte) slot);\n        });\n\n        protocol.cancelServerbound(ServerboundPackets1_21_2.PICK_ITEM);\n    }\n\n    @Override\n    public Item handleItemToClient(final UserConnection connection, Item item) {\n        item = super.handleItemToClient(connection, item);\n\n        final StructuredDataContainer dataContainer = item.dataContainer();\n        final CustomModelData1_21_4 modelData = dataContainer.get(StructuredDataKey.CUSTOM_MODEL_DATA1_21_4);\n        if (modelData != null) {\n            saveTag(createCustomTag(item), customModelDataToTag(modelData), \"custom_model_data\");\n            if (ViaBackwards.getConfig().mapCustomModelData() && modelData.floats().length > 0) {\n                // Put first float as old custom model data as this is the most common replacement\n                final int data = (int) modelData.floats()[0];\n                dataContainer.set(StructuredDataKey.CUSTOM_MODEL_DATA1_20_5, data);\n            }\n        }\n\n        downgradeItemData(item);\n        return item;\n    }\n\n    @Override\n    public Item handleItemToServer(final UserConnection connection, Item item) {\n        item = super.handleItemToServer(connection, item);\n\n        final StructuredDataContainer dataContainer = item.dataContainer();\n        final CompoundTag customData = dataContainer.get(StructuredDataKey.CUSTOM_DATA);\n        if (customData != null) {\n            if (customData.remove(nbtTagName(\"custom_model_data\")) instanceof final CompoundTag customModelData) {\n                dataContainer.set(StructuredDataKey.CUSTOM_MODEL_DATA1_21_4, customModelDataFromTag(customModelData));\n                removeCustomTag(dataContainer, customData);\n            }\n        }\n\n        updateItemData(item);\n        return item;\n    }\n\n    private CustomModelData1_21_4 customModelDataFromTag(final CompoundTag tag) {\n        final IntArrayTag floatsTag = tag.getIntArrayTag(\"floats\");\n        final float[] floats = new float[floatsTag.getValue().length];\n        for (int i = 0; i < floats.length; i++) {\n            floats[i] = Float.intBitsToFloat(floatsTag.get(i));\n        }\n\n        final ByteArrayTag booleansTag = tag.getByteArrayTag(\"booleans\");\n        final boolean[] booleans = new boolean[booleansTag.getValue().length];\n        for (int i = 0; i < booleans.length; i++) {\n            booleans[i] = booleansTag.get(i) != 0;\n        }\n\n        final ListTag<StringTag> stringsTag = tag.getListTag(\"strings\", StringTag.class);\n        final String[] strings = new String[stringsTag.size()];\n        for (int i = 0; i < strings.length; i++) {\n            strings[i] = stringsTag.get(i).getValue();\n        }\n\n        final IntArrayTag colorsTag = tag.getIntArrayTag(\"colors\");\n        return new CustomModelData1_21_4(floats, booleans, strings, colorsTag.getValue());\n    }\n\n    private CompoundTag customModelDataToTag(final CustomModelData1_21_4 customModelData) {\n        final CompoundTag tag = new CompoundTag();\n        final int[] floats = new int[customModelData.floats().length];\n        for (int i = 0; i < floats.length; i++) {\n            floats[i] = Float.floatToIntBits(customModelData.floats()[i]);\n        }\n        tag.put(\"floats\", new IntArrayTag(floats));\n\n        final byte[] booleans = new byte[customModelData.booleans().length];\n        for (int i = 0; i < booleans.length; i++) {\n            booleans[i] = (byte) (customModelData.booleans()[i] ? 1 : 0);\n        }\n        tag.put(\"booleans\", new ByteArrayTag(booleans));\n\n        final ListTag<StringTag> strings = new ListTag<>(StringTag.class);\n        for (final String string : customModelData.strings()) {\n            strings.add(new StringTag(string));\n        }\n        tag.put(\"strings\", strings);\n\n        tag.put(\"colors\", new IntArrayTag(customModelData.colors()));\n        return tag;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_4to1_21_2/rewriter/ComponentRewriter1_21_4.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_4to1_21_2.rewriter;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viabackwards.api.rewriters.text.JsonNBTComponentRewriter;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.data.StructuredDataKey;\nimport com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ClientboundPacket1_21_2;\nimport com.viaversion.viaversion.util.SerializerVersion;\n\npublic final class ComponentRewriter1_21_4 extends JsonNBTComponentRewriter<ClientboundPacket1_21_2> {\n\n    public ComponentRewriter1_21_4(final BackwardsProtocol<ClientboundPacket1_21_2, ?, ?, ?> protocol) {\n        super(protocol, ReadType.NBT);\n    }\n\n    @Override\n    protected void handleShowItem(final UserConnection connection, final CompoundTag itemTag, final CompoundTag componentsTag) {\n        super.handleShowItem(connection, itemTag, componentsTag);\n        if (componentsTag == null) {\n            return;\n        }\n\n        removeDataComponents(componentsTag, StructuredDataKey.CUSTOM_MODEL_DATA1_21_4, StructuredDataKey.TRIM1_21_4);\n    }\n\n    @Override\n    protected SerializerVersion inputSerializerVersion() {\n        return SerializerVersion.V1_21_4;\n    }\n\n    @Override\n    protected SerializerVersion outputSerializerVersion() {\n        return SerializerVersion.V1_20_5;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_4to1_21_2/rewriter/EntityPacketRewriter1_21_4.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_4to1_21_2.rewriter;\n\nimport com.viaversion.viabackwards.api.rewriters.EntityRewriter;\nimport com.viaversion.viabackwards.protocol.v1_21_4to1_21_2.Protocol1_21_4To1_21_2;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityType;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_21_4;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.version.VersionedTypes;\nimport com.viaversion.viaversion.protocols.v1_21_2to1_21_4.packet.ServerboundPackets1_21_4;\nimport com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ClientboundPacket1_21_2;\nimport com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ClientboundPackets1_21_2;\nimport com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ServerboundPackets1_21_2;\n\npublic final class EntityPacketRewriter1_21_4 extends EntityRewriter<ClientboundPacket1_21_2, Protocol1_21_4To1_21_2> {\n\n    public EntityPacketRewriter1_21_4(final Protocol1_21_4To1_21_2 protocol) {\n        super(protocol, VersionedTypes.V1_21_4.entityDataTypes.optionalComponentType, VersionedTypes.V1_21_4.entityDataTypes.booleanType);\n    }\n\n    @Override\n    public void registerPackets() {\n        protocol.replaceClientbound(ClientboundPackets1_21_2.LOGIN, wrapper -> {\n            final int entityId = wrapper.passthrough(Types.INT); // Entity id\n            wrapper.passthrough(Types.BOOLEAN); // Hardcore\n            wrapper.passthrough(Types.STRING_ARRAY); // World List\n            wrapper.passthrough(Types.VAR_INT); // Max players\n            wrapper.passthrough(Types.VAR_INT); // View distance\n            wrapper.passthrough(Types.VAR_INT); // Simulation distance\n            wrapper.passthrough(Types.BOOLEAN); // Reduced debug info\n            wrapper.passthrough(Types.BOOLEAN); // Show death screen\n            wrapper.passthrough(Types.BOOLEAN); // Limited crafting\n            final int dimensionId = wrapper.passthrough(Types.VAR_INT);\n            final String world = wrapper.passthrough(Types.STRING);\n            trackWorldDataByKey1_20_5(wrapper.user(), dimensionId, world);\n            trackPlayer(wrapper.user(), entityId);\n\n            final PacketWrapper playerLoadedPacket = wrapper.create(ServerboundPackets1_21_4.PLAYER_LOADED);\n            playerLoadedPacket.scheduleSendToServer(Protocol1_21_4To1_21_2.class);\n        });\n\n        protocol.replaceClientbound(ClientboundPackets1_21_2.RESPAWN, wrapper -> {\n            final int dimensionId = wrapper.passthrough(Types.VAR_INT);\n            final String world = wrapper.passthrough(Types.STRING);\n            trackWorldDataByKey1_20_5(wrapper.user(), dimensionId, world);\n\n            final PacketWrapper playerLoadedPacket = wrapper.create(ServerboundPackets1_21_4.PLAYER_LOADED);\n            playerLoadedPacket.scheduleSendToServer(Protocol1_21_4To1_21_2.class);\n        });\n\n        protocol.registerServerbound(ServerboundPackets1_21_2.MOVE_VEHICLE, wrapper -> {\n            wrapper.passthrough(Types.DOUBLE); // X\n            wrapper.passthrough(Types.DOUBLE); // Y\n            wrapper.passthrough(Types.DOUBLE); // Z\n            wrapper.passthrough(Types.FLOAT); // Yaw\n            wrapper.passthrough(Types.FLOAT); // Pitch\n            wrapper.write(Types.BOOLEAN, true); // On ground // TODO ...\n        });\n    }\n\n    @Override\n    protected void registerRewrites() {\n        dataTypeMapper().register();\n        registerEntityDataTypeHandler1_20_3(\n            VersionedTypes.V1_21_2.entityDataTypes.itemType,\n            VersionedTypes.V1_21_2.entityDataTypes.blockStateType,\n            VersionedTypes.V1_21_2.entityDataTypes.optionalBlockStateType,\n            VersionedTypes.V1_21_2.entityDataTypes.particleType,\n            VersionedTypes.V1_21_2.entityDataTypes.particlesType,\n            VersionedTypes.V1_21_2.entityDataTypes.componentType,\n            VersionedTypes.V1_21_2.entityDataTypes.optionalComponentType\n        );\n        registerBlockStateHandler(EntityTypes1_21_4.ABSTRACT_MINECART, 11);\n\n        filter().type(EntityTypes1_21_4.CREAKING).removeIndex(19); // Home pos\n        filter().type(EntityTypes1_21_4.CREAKING).removeIndex(18); // Is tearing down\n        filter().type(EntityTypes1_21_4.SALMON).index(17).handler((event, data) -> {\n            final int typeId = data.value();\n            final String type = switch (typeId) {\n                case 0 -> \"small\";\n                case 2 -> \"large\";\n                default -> \"medium\";\n            };\n            data.setTypeAndValue(VersionedTypes.V1_21_4.entityDataTypes.stringType, type);\n        });\n    }\n\n    @Override\n    public EntityType typeFromId(final int type) {\n        return EntityTypes1_21_4.getTypeFromId(type);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_4to1_21_2/rewriter/ParticleRewriter1_21_4.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_4to1_21_2.rewriter;\n\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.Particle;\nimport com.viaversion.viaversion.api.protocol.Protocol;\nimport com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ClientboundPacket1_21_2;\nimport com.viaversion.viaversion.rewriter.ParticleRewriter;\n\npublic final class ParticleRewriter1_21_4 extends ParticleRewriter<ClientboundPacket1_21_2> {\n\n    public ParticleRewriter1_21_4(final Protocol<ClientboundPacket1_21_2, ?, ?, ?> protocol) {\n        super(protocol);\n    }\n\n    @Override\n    public void rewriteParticle(final UserConnection connection, final Particle particle) {\n        super.rewriteParticle(connection, particle);\n\n        final String identifier = protocol.getMappingData().getParticleMappings().mappedIdentifier(particle.id());\n        if (identifier.equals(\"minecraft:trail\")) {\n            particle.removeArgument(4); // Duration\n        }\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_5to1_21_4/Protocol1_21_5To1_21_4.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_5to1_21_4;\n\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viabackwards.api.data.BackwardsMappingData;\nimport com.viaversion.viabackwards.api.rewriters.BackwardsRegistryRewriter;\nimport com.viaversion.viabackwards.protocol.v1_21_5to1_21_4.rewriter.BlockItemPacketRewriter1_21_5;\nimport com.viaversion.viabackwards.protocol.v1_21_5to1_21_4.rewriter.BlockPacketRewriter1_21_5;\nimport com.viaversion.viabackwards.protocol.v1_21_5to1_21_4.rewriter.ComponentRewriter1_21_5;\nimport com.viaversion.viabackwards.protocol.v1_21_5to1_21_4.rewriter.EntityPacketRewriter1_21_5;\nimport com.viaversion.viabackwards.protocol.v1_21_5to1_21_4.rewriter.RegistryDataRewriter1_21_5;\nimport com.viaversion.viabackwards.protocol.v1_21_5to1_21_4.storage.HashedItemConverterStorage;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.Particle;\nimport com.viaversion.viaversion.api.minecraft.data.version.StructuredDataKeys1_21_2;\nimport com.viaversion.viaversion.api.minecraft.data.version.StructuredDataKeys1_21_5;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_21_4;\nimport com.viaversion.viaversion.api.minecraft.entitydata.types.EntityDataTypes1_21_2;\nimport com.viaversion.viaversion.api.minecraft.entitydata.types.EntityDataTypes1_21_5;\nimport com.viaversion.viaversion.api.minecraft.item.data.ArmorTrimPattern;\nimport com.viaversion.viaversion.api.minecraft.item.data.ChatType;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.protocol.packet.provider.PacketTypesProvider;\nimport com.viaversion.viaversion.api.protocol.packet.provider.SimplePacketTypesProvider;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.version.Types1_20_5;\nimport com.viaversion.viaversion.api.type.types.version.VersionedTypes;\nimport com.viaversion.viaversion.data.entity.EntityTrackerBase;\nimport com.viaversion.viaversion.protocols.v1_19_3to1_19_4.rewriter.CommandRewriter1_19_4;\nimport com.viaversion.viaversion.protocols.v1_20_3to1_20_5.packet.ServerboundConfigurationPackets1_20_5;\nimport com.viaversion.viaversion.protocols.v1_20_5to1_21.packet.ClientboundConfigurationPackets1_21;\nimport com.viaversion.viaversion.protocols.v1_21_2to1_21_4.packet.ServerboundPacket1_21_4;\nimport com.viaversion.viaversion.protocols.v1_21_2to1_21_4.packet.ServerboundPackets1_21_4;\nimport com.viaversion.viaversion.protocols.v1_21_4to1_21_5.Protocol1_21_4To1_21_5;\nimport com.viaversion.viaversion.protocols.v1_21_4to1_21_5.packet.ClientboundPacket1_21_5;\nimport com.viaversion.viaversion.protocols.v1_21_4to1_21_5.packet.ClientboundPackets1_21_5;\nimport com.viaversion.viaversion.protocols.v1_21_4to1_21_5.packet.ServerboundPacket1_21_5;\nimport com.viaversion.viaversion.protocols.v1_21_4to1_21_5.packet.ServerboundPackets1_21_5;\nimport com.viaversion.viaversion.protocols.v1_21_4to1_21_5.rewriter.RecipeDisplayRewriter1_21_5;\nimport com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ClientboundPacket1_21_2;\nimport com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ClientboundPackets1_21_2;\nimport com.viaversion.viaversion.rewriter.BlockRewriter;\nimport com.viaversion.viaversion.rewriter.ParticleRewriter;\nimport com.viaversion.viaversion.rewriter.RecipeDisplayRewriter;\nimport com.viaversion.viaversion.rewriter.TagRewriter;\nimport com.viaversion.viaversion.util.Key;\nimport com.viaversion.viaversion.util.Limit;\n\nimport static com.viaversion.viaversion.util.ProtocolUtil.packetTypeMap;\n\npublic final class Protocol1_21_5To1_21_4 extends BackwardsProtocol<ClientboundPacket1_21_5, ClientboundPacket1_21_2, ServerboundPacket1_21_5, ServerboundPacket1_21_4> {\n\n    public static final BackwardsMappingData MAPPINGS = new BackwardsMappingData(\"1.21.5\", \"1.21.4\", Protocol1_21_4To1_21_5.class);\n    private final EntityPacketRewriter1_21_5 entityRewriter = new EntityPacketRewriter1_21_5(this);\n    private final BlockItemPacketRewriter1_21_5 itemRewriter = new BlockItemPacketRewriter1_21_5(this);\n    private final ParticleRewriter<ClientboundPacket1_21_5> particleRewriter = new ParticleRewriter<>(this) {\n        @Override\n        public void rewriteParticle(final UserConnection connection, final Particle particle) {\n            if (particle.id() == MAPPINGS.getParticleMappings().id(\"tinted_leaves\")) {\n                particle.getArguments().clear();\n            }\n            super.rewriteParticle(connection, particle);\n        }\n    };\n    private final ComponentRewriter1_21_5 translatableRewriter = new ComponentRewriter1_21_5(this);\n    private final TagRewriter<ClientboundPacket1_21_5> tagRewriter = new TagRewriter<>(this);\n    private final RecipeDisplayRewriter<ClientboundPacket1_21_5> recipeRewriter = new RecipeDisplayRewriter1_21_5<>(this) {\n        @Override\n        protected void handleSmithingTrimSlotDisplay(final PacketWrapper wrapper) {\n            handleSlotDisplay(wrapper); // Base\n            handleSlotDisplay(wrapper); // Material\n            wrapper.read(ArmorTrimPattern.TYPE1_21_5); // Pattern\n            // Add empty slot display...\n            wrapper.write(Types.VAR_INT, 0);\n        }\n    };\n    private final BackwardsRegistryRewriter registryDataRewriter = new RegistryDataRewriter1_21_5(this);\n    private final BlockRewriter<ClientboundPacket1_21_5> blockRewriter = new BlockPacketRewriter1_21_5(this);\n\n    public Protocol1_21_5To1_21_4() {\n        super(ClientboundPacket1_21_5.class, ClientboundPacket1_21_2.class, ServerboundPacket1_21_5.class, ServerboundPacket1_21_4.class);\n    }\n\n    @Override\n    protected void registerPackets() {\n        super.registerPackets();\n\n        final CommandRewriter1_19_4<ClientboundPacket1_21_5> commandRewriter = new CommandRewriter1_19_4<>(this) {\n            @Override\n            public void handleArgument(final PacketWrapper wrapper, final String argumentType) {\n                if (argumentType.equals(\"minecraft:resource\")) {\n                    String resource = wrapper.read(Types.STRING);\n                    if (Key.equals(resource, \"test_instance\")) {\n                        resource = \"minecraft:item\"; // Just give it anything else\n                    }\n                    wrapper.write(Types.STRING, resource);\n                } else if (argumentType.equals(\"minecraft:resource_selector\")) {\n                    wrapper.read(Types.STRING); // Selector\n                    wrapper.write(Types.VAR_INT, 1); // Quotable string\n                } else {\n                    super.handleArgument(wrapper, argumentType);\n                }\n            }\n        };\n        replaceClientbound(ClientboundPackets1_21_5.COMMANDS, commandRewriter::handle1_19);\n\n        replaceClientbound(ClientboundPackets1_21_5.PLAYER_CHAT, wrapper -> {\n            wrapper.read(Types.VAR_INT); // Index\n\n            wrapper.passthrough(Types.UUID); // Sender\n            wrapper.passthrough(Types.VAR_INT); // Index\n            wrapper.passthrough(Types.OPTIONAL_SIGNATURE_BYTES); // Signature\n            wrapper.passthrough(Types.STRING); // Plain content\n            wrapper.passthrough(Types.LONG); // Timestamp\n            wrapper.passthrough(Types.LONG); // Salt\n\n            final int lastSeen = wrapper.passthrough(Types.VAR_INT);\n            for (int i = 0; i < lastSeen; i++) {\n                final int index = wrapper.passthrough(Types.VAR_INT);\n                if (index == 0) {\n                    wrapper.passthrough(Types.SIGNATURE_BYTES);\n                }\n            }\n\n            translatableRewriter.processTag(wrapper.user(), wrapper.passthrough(Types.TRUSTED_OPTIONAL_TAG)); // Unsigned content\n\n            final int filterMaskType = wrapper.passthrough(Types.VAR_INT);\n            if (filterMaskType == 2) { // Partially filtered\n                wrapper.passthrough(Types.LONG_ARRAY_PRIMITIVE); // Mask\n            }\n\n            wrapper.passthrough(ChatType.TYPE); // Chat Type\n            translatableRewriter.processTag(wrapper.user(), wrapper.passthrough(Types.TRUSTED_TAG)); // Name\n            translatableRewriter.processTag(wrapper.user(), wrapper.passthrough(Types.TRUSTED_OPTIONAL_TAG)); // Target Name\n        });\n        registerServerbound(ServerboundPackets1_21_4.CHAT_COMMAND_SIGNED, wrapper -> {\n            wrapper.passthrough(Types.STRING); // Command\n            wrapper.passthrough(Types.LONG); // Timestamp\n            wrapper.passthrough(Types.LONG); // Salt\n            final int signatures = Limit.max(wrapper.passthrough(Types.VAR_INT), 8);\n            for (int i = 0; i < signatures; i++) {\n                wrapper.passthrough(Types.STRING); // Argument name\n                wrapper.passthrough(Types.SIGNATURE_BYTES); // Signature\n            }\n            wrapper.passthrough(Types.VAR_INT); // Offset\n            wrapper.passthrough(Types.ACKNOWLEDGED_BIT_SET); // Acknowledged\n            wrapper.write(Types.BYTE, (byte) 0); // Checksum\n        });\n        registerServerbound(ServerboundPackets1_21_4.CHAT, wrapper -> {\n            wrapper.passthrough(Types.STRING); // Message\n            wrapper.passthrough(Types.LONG); // Timestamp\n            wrapper.passthrough(Types.LONG); // Salt\n            wrapper.passthrough(Types.OPTIONAL_SIGNATURE_BYTES); // Signature\n            wrapper.passthrough(Types.VAR_INT); // Offset\n            wrapper.passthrough(Types.ACKNOWLEDGED_BIT_SET); // Acknowledged\n            wrapper.write(Types.BYTE, (byte) 0); // Checksum\n        });\n\n        cancelClientbound(ClientboundPackets1_21_5.TEST_INSTANCE_BLOCK_STATUS);\n    }\n\n    @Override\n    public void init(final UserConnection user) {\n        addEntityTracker(user, new EntityTrackerBase(user, EntityTypes1_21_4.PLAYER));\n        user.put(new HashedItemConverterStorage(this));\n    }\n\n    @Override\n    public BackwardsMappingData getMappingData() {\n        return MAPPINGS;\n    }\n\n    @Override\n    public EntityPacketRewriter1_21_5 getEntityRewriter() {\n        return entityRewriter;\n    }\n\n    @Override\n    public BlockItemPacketRewriter1_21_5 getItemRewriter() {\n        return itemRewriter;\n    }\n\n    @Override\n    public BlockRewriter<ClientboundPacket1_21_5> getBlockRewriter() {\n        return blockRewriter;\n    }\n\n    @Override\n    public BackwardsRegistryRewriter getRegistryDataRewriter() {\n        return registryDataRewriter;\n    }\n\n    @Override\n    public ParticleRewriter<ClientboundPacket1_21_5> getParticleRewriter() {\n        return particleRewriter;\n    }\n\n    @Override\n    public ComponentRewriter1_21_5 getComponentRewriter() {\n        return translatableRewriter;\n    }\n\n    @Override\n    public TagRewriter<ClientboundPacket1_21_5> getTagRewriter() {\n        return tagRewriter;\n    }\n\n    @Override\n    public RecipeDisplayRewriter<ClientboundPacket1_21_5> getRecipeRewriter() {\n        return recipeRewriter;\n    }\n\n    @Override\n    public Types1_20_5<StructuredDataKeys1_21_5, EntityDataTypes1_21_5> types() {\n        return VersionedTypes.V1_21_5;\n    }\n\n    @Override\n    public Types1_20_5<StructuredDataKeys1_21_2, EntityDataTypes1_21_2> mappedTypes() {\n        return VersionedTypes.V1_21_4;\n    }\n\n    @Override\n    protected PacketTypesProvider<ClientboundPacket1_21_5, ClientboundPacket1_21_2, ServerboundPacket1_21_5, ServerboundPacket1_21_4> createPacketTypesProvider() {\n        return new SimplePacketTypesProvider<>(\n            packetTypeMap(unmappedClientboundPacketType, ClientboundPackets1_21_5.class, ClientboundConfigurationPackets1_21.class),\n            packetTypeMap(mappedClientboundPacketType, ClientboundPackets1_21_2.class, ClientboundConfigurationPackets1_21.class),\n            packetTypeMap(mappedServerboundPacketType, ServerboundPackets1_21_5.class, ServerboundConfigurationPackets1_20_5.class),\n            packetTypeMap(unmappedServerboundPacketType, ServerboundPackets1_21_4.class, ServerboundConfigurationPackets1_20_5.class)\n        );\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_5to1_21_4/rewriter/BlockItemPacketRewriter1_21_5.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_5to1_21_4.rewriter;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.FloatTag;\nimport com.viaversion.nbt.tag.IntArrayTag;\nimport com.viaversion.nbt.tag.IntTag;\nimport com.viaversion.nbt.tag.ListTag;\nimport com.viaversion.nbt.tag.LongArrayTag;\nimport com.viaversion.nbt.tag.Tag;\nimport com.viaversion.viabackwards.api.rewriters.BackwardsStructuredItemRewriter;\nimport com.viaversion.viabackwards.protocol.v1_21_5to1_21_4.Protocol1_21_5To1_21_4;\nimport com.viaversion.viabackwards.protocol.v1_21_5to1_21_4.storage.HashedItemConverterStorage;\nimport com.viaversion.viabackwards.protocol.v1_21_5to1_21_4.storage.HorseDataStorage;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.data.Mappings;\nimport com.viaversion.viaversion.api.data.entity.EntityTracker;\nimport com.viaversion.viaversion.api.data.entity.TrackedEntity;\nimport com.viaversion.viaversion.api.minecraft.Holder;\nimport com.viaversion.viaversion.api.minecraft.HolderSet;\nimport com.viaversion.viaversion.api.minecraft.PaintingVariant;\nimport com.viaversion.viaversion.api.minecraft.SoundEvent;\nimport com.viaversion.viaversion.api.minecraft.chunks.Chunk;\nimport com.viaversion.viaversion.api.minecraft.chunks.Chunk1_18;\nimport com.viaversion.viaversion.api.minecraft.chunks.DataPalette;\nimport com.viaversion.viaversion.api.minecraft.chunks.Heightmap;\nimport com.viaversion.viaversion.api.minecraft.data.StructuredDataContainer;\nimport com.viaversion.viaversion.api.minecraft.data.StructuredDataKey;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_21_5;\nimport com.viaversion.viaversion.api.minecraft.entitydata.EntityData;\nimport com.viaversion.viaversion.api.minecraft.item.Item;\nimport com.viaversion.viaversion.api.minecraft.item.data.ArmorTrimMaterial;\nimport com.viaversion.viaversion.api.minecraft.item.data.BlocksAttacks;\nimport com.viaversion.viaversion.api.minecraft.item.data.BlocksAttacks.DamageReduction;\nimport com.viaversion.viaversion.api.minecraft.item.data.BlocksAttacks.ItemDamageFunction;\nimport com.viaversion.viaversion.api.minecraft.item.data.Equippable;\nimport com.viaversion.viaversion.api.minecraft.item.data.ProvidesTrimMaterial;\nimport com.viaversion.viaversion.api.minecraft.item.data.ToolProperties;\nimport com.viaversion.viaversion.api.minecraft.item.data.TooltipDisplay;\nimport com.viaversion.viaversion.api.minecraft.item.data.TropicalFishPattern;\nimport com.viaversion.viaversion.api.minecraft.item.data.Weapon;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.rewriter.ComponentRewriter;\nimport com.viaversion.viaversion.api.type.Type;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.chunk.ChunkBiomesType1_19_4;\nimport com.viaversion.viaversion.api.type.types.chunk.ChunkBiomesType1_21_5;\nimport com.viaversion.viaversion.api.type.types.chunk.ChunkType1_20_2;\nimport com.viaversion.viaversion.api.type.types.chunk.ChunkType1_21_5;\nimport com.viaversion.viaversion.api.type.types.version.VersionedTypes;\nimport com.viaversion.viaversion.data.item.ItemHasherBase;\nimport com.viaversion.viaversion.libs.fastutil.ints.IntArrayList;\nimport com.viaversion.viaversion.libs.fastutil.ints.IntLinkedOpenHashSet;\nimport com.viaversion.viaversion.libs.fastutil.ints.IntList;\nimport com.viaversion.viaversion.protocols.v1_21_2to1_21_4.packet.ServerboundPacket1_21_4;\nimport com.viaversion.viaversion.protocols.v1_21_2to1_21_4.packet.ServerboundPackets1_21_4;\nimport com.viaversion.viaversion.protocols.v1_21_4to1_21_5.packet.ClientboundPacket1_21_5;\nimport com.viaversion.viaversion.protocols.v1_21_4to1_21_5.packet.ClientboundPackets1_21_5;\nimport com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ClientboundPackets1_21_2;\nimport com.viaversion.viaversion.util.Either;\nimport com.viaversion.viaversion.util.Key;\nimport com.viaversion.viaversion.util.Limit;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport static com.viaversion.viaversion.protocols.v1_21_4to1_21_5.rewriter.BlockItemPacketRewriter1_21_5.downgradeItemData;\nimport static com.viaversion.viaversion.protocols.v1_21_4to1_21_5.rewriter.BlockItemPacketRewriter1_21_5.updateItemData;\nimport static com.viaversion.viaversion.util.MathUtil.ceilLog2;\n\npublic final class BlockItemPacketRewriter1_21_5 extends BackwardsStructuredItemRewriter<ClientboundPacket1_21_5, ServerboundPacket1_21_4, Protocol1_21_5To1_21_4> {\n    private static final int SADDLE_EQUIPMENT_SLOT = 7;\n    static final byte SADDLED_FLAG = 4;\n\n    public BlockItemPacketRewriter1_21_5(final Protocol1_21_5To1_21_4 protocol) {\n        super(protocol);\n    }\n\n    @Override\n    public void registerPackets() {\n        protocol.replaceClientbound(ClientboundPackets1_21_5.LEVEL_CHUNK_WITH_LIGHT, wrapper -> {\n            final EntityTracker tracker = protocol.getEntityRewriter().tracker(wrapper.user());\n            final Mappings blockStateMappings = protocol.getMappingData().getBlockStateMappings();\n            final Type<Chunk> chunkType = new ChunkType1_21_5(tracker.currentWorldSectionHeight(), ceilLog2(blockStateMappings.size()), ceilLog2(tracker.biomesSent()));\n            final Chunk chunk = wrapper.read(chunkType);\n            protocol.getBlockRewriter().handleChunk(chunk);\n            protocol.getBlockRewriter().handleBlockEntities(chunk, wrapper.user());\n\n            final Type<Chunk> newChunkType = new ChunkType1_20_2(tracker.currentWorldSectionHeight(), ceilLog2(blockStateMappings.mappedSize()), ceilLog2(tracker.biomesSent()));\n            final CompoundTag heightmapTag = new CompoundTag();\n            for (final Heightmap heightmap : chunk.heightmaps()) {\n                final String typeKey = heightmapType(heightmap.type());\n                if (typeKey == null) {\n                    protocol.getLogger().warning(\"Unknown heightmap type id: \" + heightmap.type());\n                    continue;\n                }\n\n                heightmapTag.put(typeKey, new LongArrayTag(heightmap.data()));\n            }\n\n            final Chunk mappedChunk = new Chunk1_18(chunk.getX(), chunk.getZ(), chunk.getSections(), heightmapTag, chunk.blockEntities());\n            wrapper.write(newChunkType, mappedChunk);\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_21_5.CHUNKS_BIOMES, wrapper -> {\n            final EntityTracker tracker = protocol.getEntityRewriter().tracker(wrapper.user());\n            final int globalPaletteBiomeBits = ceilLog2(tracker.biomesSent());\n            final Type<DataPalette[]> biomesType = new ChunkBiomesType1_21_5(tracker.currentWorldSectionHeight(), globalPaletteBiomeBits);\n            final Type<DataPalette[]> newBiomesType = new ChunkBiomesType1_19_4(tracker.currentWorldSectionHeight(), globalPaletteBiomeBits);\n\n            final int size = wrapper.passthrough(Types.VAR_INT);\n            for (int i = 0; i < size; i++) {\n                wrapper.passthrough(Types.CHUNK_POSITION);\n                wrapper.passthroughAndMap(biomesType, newBiomesType);\n            }\n        });\n\n        protocol.replaceServerbound(ServerboundPackets1_21_4.SET_CREATIVE_MODE_SLOT, wrapper -> {\n            wrapper.passthrough(Types.SHORT); // Slot\n\n            final Item item = handleItemToServer(wrapper.user(), wrapper.read(mappedItemType()));\n            wrapper.write(VersionedTypes.V1_21_5.lengthPrefixedItem(), item);\n        });\n\n        protocol.replaceServerbound(ServerboundPackets1_21_4.CONTAINER_CLICK, wrapper -> {\n            wrapper.passthrough(Types.VAR_INT); // Container id\n            wrapper.passthrough(Types.VAR_INT); // State id\n            wrapper.passthrough(Types.SHORT); // Slot\n            wrapper.passthrough(Types.BYTE); // Button\n            wrapper.passthrough(Types.VAR_INT); // Mode\n\n            // Try our best to get a hashed item out of it - will be wrong for some data component types that don't have their conversion implemented\n            final HashedItemConverterStorage hashedItemConverter = wrapper.user().get(HashedItemConverterStorage.class);\n            final int affectedItems = Limit.max(wrapper.passthrough(Types.VAR_INT), 128);\n            for (int i = 0; i < affectedItems; i++) {\n                wrapper.passthrough(Types.SHORT); // Slot\n                final Item item = handleItemToServer(wrapper.user(), wrapper.read(mappedItemType()));\n                wrapper.write(Types.HASHED_ITEM, ItemHasherBase.toHashedItem(hashedItemConverter.hasher(), item));\n            }\n\n            final Item carriedItem = handleItemToServer(wrapper.user(), wrapper.read(mappedItemType()));\n            wrapper.write(Types.HASHED_ITEM, ItemHasherBase.toHashedItem(hashedItemConverter.hasher(), carriedItem));\n        });\n\n        protocol.replaceClientbound(ClientboundPackets1_21_5.SET_EQUIPMENT, wrapper -> {\n            final int entityId = wrapper.passthrough(Types.VAR_INT);\n            final TrackedEntity trackedEntity = protocol.getEntityRewriter().tracker(wrapper.user()).entity(entityId);\n\n            // Remove saddle equipment, keep the rest\n            final IntList keptSlots = new IntArrayList();\n            final List<Item> keptItems = new ArrayList<>();\n            byte value;\n            do {\n                value = wrapper.read(Types.BYTE);\n                final int equipmentSlot = value & 0x7F;\n                final Item item = wrapper.read(itemType());\n                if (equipmentSlot == SADDLE_EQUIPMENT_SLOT) {\n                    // Send saddle entity data for horses\n                    if (trackedEntity != null && (\n                        trackedEntity.entityType().isOrHasParent(EntityTypes1_21_5.ABSTRACT_HORSE)\n                            || trackedEntity.entityType().isOrHasParent(EntityTypes1_21_5.PIG)\n                            || trackedEntity.entityType().isOrHasParent(EntityTypes1_21_5.STRIDER)\n                    )) {\n                        sendSaddledEntityData(wrapper.user(), trackedEntity, entityId, item.identifier() == 800);\n                    }\n                } else {\n                    keptSlots.add(equipmentSlot);\n                    keptItems.add(handleItemToClient(wrapper.user(), item));\n                }\n            } while (value < 0);\n\n            if (keptSlots.isEmpty()) {\n                wrapper.cancel();\n                return;\n            }\n\n            for (int i = 0; i < keptSlots.size(); i++) {\n                final int slot = keptSlots.getInt(i);\n                final boolean more = i < keptSlots.size() - 1;\n                wrapper.write(Types.BYTE, (byte) (more ? (slot | -128) : slot));\n                wrapper.write(mappedItemType(), keptItems.get(i));\n            }\n        });\n\n        protocol.replaceClientbound(ClientboundPackets1_21_5.UPDATE_ADVANCEMENTS, wrapper -> {\n            wrapper.passthrough(Types.BOOLEAN); // Reset/clear\n            int size = wrapper.passthrough(Types.VAR_INT); // Mapping size\n            for (int i = 0; i < size; i++) {\n                wrapper.passthrough(Types.STRING); // Identifier\n                wrapper.passthrough(Types.OPTIONAL_STRING); // Parent\n\n                // Display data\n                if (wrapper.passthrough(Types.BOOLEAN)) {\n                    final Tag title = wrapper.passthrough(Types.TRUSTED_TAG);\n                    final Tag description = wrapper.passthrough(Types.TRUSTED_TAG);\n                    final ComponentRewriter componentRewriter = protocol.getComponentRewriter();\n                    if (componentRewriter != null) {\n                        componentRewriter.processTag(wrapper.user(), title);\n                        componentRewriter.processTag(wrapper.user(), description);\n                    }\n\n                    passthroughClientboundItem(wrapper); // Icon\n                    wrapper.passthrough(Types.VAR_INT); // Frame type\n                    int flags = wrapper.passthrough(Types.INT); // Flags\n                    if ((flags & 1) != 0) {\n                        convertClientAsset(wrapper);\n                    }\n                    wrapper.passthrough(Types.FLOAT); // X\n                    wrapper.passthrough(Types.FLOAT); // Y\n                }\n\n                int requirements = wrapper.passthrough(Types.VAR_INT);\n                for (int array = 0; array < requirements; array++) {\n                    wrapper.passthrough(Types.STRING_ARRAY);\n                }\n\n                wrapper.passthrough(Types.BOOLEAN); // Send telemetry\n            }\n\n            wrapper.passthrough(Types.STRING_ARRAY); // Removed\n            final int progressSize = wrapper.passthrough(Types.VAR_INT);\n            for (int i = 0; i < progressSize; i++) {\n                wrapper.passthrough(Types.STRING); // Key\n\n                final int criterionSize = wrapper.passthrough(Types.VAR_INT);\n                for (int j = 0; j < criterionSize; j++) {\n                    wrapper.passthrough(Types.STRING); // Key\n                    wrapper.passthrough(Types.OPTIONAL_LONG); // Obtained instant\n                }\n            }\n\n            wrapper.read(Types.BOOLEAN); // Show advancements\n        });\n\n    }\n\n    private void convertClientAsset(final PacketWrapper wrapper) {\n        final String background = wrapper.read(Types.STRING);\n        final String namespace = Key.namespace(background);\n        final String path = Key.stripNamespace(background);\n        wrapper.write(Types.STRING, namespace + \":textures/\" + path + \".png\");\n    }\n\n    private void sendSaddledEntityData(final UserConnection connection, final TrackedEntity trackedEntity, final int entityId, final boolean saddled) {\n        EntityData entityData = null;\n\n        if (trackedEntity.entityType().isOrHasParent(EntityTypes1_21_5.ABSTRACT_HORSE)) {\n            byte data = 0;\n            if (trackedEntity.hasData()) {\n                final HorseDataStorage horseDataStorage = trackedEntity.data().get(HorseDataStorage.class);\n                if (horseDataStorage != null) {\n                    if (horseDataStorage.saddled() == saddled) {\n                        return;\n                    }\n\n                    data = horseDataStorage.data();\n                }\n            }\n\n            trackedEntity.data().put(new HorseDataStorage(data, saddled));\n\n            if (saddled) {\n                data = (byte) (data | SADDLED_FLAG);\n            }\n\n            entityData = new EntityData(17, VersionedTypes.V1_21_4.entityDataTypes.byteType, data);\n\n        } else if (trackedEntity.entityType().isOrHasParent(EntityTypes1_21_5.PIG)) {\n            entityData = new EntityData(17, VersionedTypes.V1_21_4.entityDataTypes.booleanType, saddled);\n\n        } else if (trackedEntity.entityType().isOrHasParent(EntityTypes1_21_5.STRIDER)) {\n            entityData = new EntityData(19, VersionedTypes.V1_21_4.entityDataTypes.booleanType, saddled);\n        }\n\n        if (entityData != null) {\n            final PacketWrapper entityDataPacket = PacketWrapper.create(ClientboundPackets1_21_2.SET_ENTITY_DATA, connection);\n            final List<EntityData> entityDataList = new ArrayList<>();\n            entityDataList.add(entityData);\n            entityDataPacket.write(Types.VAR_INT, entityId);\n            entityDataPacket.write(VersionedTypes.V1_21_4.entityDataList, entityDataList);\n            entityDataPacket.send(Protocol1_21_5To1_21_4.class);\n        }\n    }\n\n    private String heightmapType(final int id) {\n        return switch (id) {\n            case 0 -> \"WORLD_SURFACE_WG\";\n            case 1 -> \"WORLD_SURFACE\";\n            case 2 -> \"OCEAN_FLOOR_WG\";\n            case 3 -> \"OCEAN_FLOOR\";\n            case 4 -> \"MOTION_BLOCKING\";\n            case 5 -> \"MOTION_BLOCKING_NO_LEAVES\";\n            default -> null;\n        };\n    }\n\n    @Override\n    protected void backupInconvertibleData(final UserConnection connection, final Item item, final StructuredDataContainer dataContainer, final CompoundTag backupTag) {\n        super.backupInconvertibleData(connection, item, dataContainer, backupTag);\n        final ToolProperties toolProperties = dataContainer.get(StructuredDataKey.TOOL1_21_5);\n        if (toolProperties != null && toolProperties.canDestroyBlocksInCreative()) {\n            backupTag.putBoolean(\"tool\", true);\n        }\n\n        final Equippable equippable = dataContainer.get(StructuredDataKey.EQUIPPABLE1_21_5);\n        if (equippable != null && equippable.equipOnInteract()) {\n            backupTag.putBoolean(\"equippable\", true);\n        }\n\n        final Weapon weapon = dataContainer.get(StructuredDataKey.WEAPON);\n        if (weapon != null) {\n            final CompoundTag weaponTag = new CompoundTag();\n            backupTag.put(\"weapon\", weaponTag);\n            weaponTag.putInt(\"item_damage_per_attack\", weapon.itemDamagePerAttack());\n            weaponTag.putFloat(\"disable_blocking_for_seconds\", weapon.disableBlockingForSeconds());\n        }\n\n        final ProvidesTrimMaterial providesTrimMaterial = dataContainer.get(StructuredDataKey.PROVIDES_TRIM_MATERIAL1_21_5);\n        if (providesTrimMaterial != null) {\n            final Tag materialTag = eitherHolderToTag(providesTrimMaterial.material(), (material, tag) -> {\n                tag.putString(\"asset_name\", material.assetName());\n                tag.putInt(\"item_id\", material.itemId());\n                tag.putFloat(\"item_model_index\", material.itemModelIndex());\n                final CompoundTag overrideArmorMaterials = new CompoundTag();\n                material.overrideArmorMaterials().forEach(overrideArmorMaterials::putString);\n                tag.put(\"override_armor_materials\", overrideArmorMaterials);\n                tag.put(\"description\", material.description());\n            });\n            backupTag.put(\"provides_trim_material\", materialTag);\n        }\n\n        final BlocksAttacks blocksAttacks = dataContainer.get(StructuredDataKey.BLOCKS_ATTACKS1_21_5);\n        if (blocksAttacks != null) {\n            final CompoundTag blocksAttackTag = new CompoundTag();\n            backupTag.put(\"blocks_attack\", blocksAttackTag);\n            blocksAttackTag.putFloat(\"block_delay_seconds\", blocksAttacks.blockDelaySeconds());\n            blocksAttackTag.putFloat(\"disable_cooldown_scale\", blocksAttacks.disableCooldownScale());\n\n            final ListTag<CompoundTag> damageReductions = new ListTag<>(CompoundTag.class);\n            blocksAttackTag.put(\"damage_reductions\", damageReductions);\n            for (final DamageReduction damageReduction : blocksAttacks.damageReductions()) {\n                final CompoundTag damageReductionTag = new CompoundTag();\n                damageReductionTag.putFloat(\"horizontal_blocking_angle\", damageReduction.horizontalBlockingAngle());\n                if (damageReduction.type() != null) {\n                    damageReductionTag.put(\"type\", holderSetToTag(damageReduction.type()));\n                }\n                damageReductionTag.putFloat(\"base\", damageReduction.base());\n                damageReductionTag.putFloat(\"factor\", damageReduction.factor());\n                damageReductions.add(damageReductionTag);\n            }\n\n            final CompoundTag itemDamageTag = new CompoundTag();\n            blocksAttackTag.put(\"item_damage\", itemDamageTag);\n            itemDamageTag.putFloat(\"threshold\", blocksAttacks.itemDamage().threshold());\n            itemDamageTag.putFloat(\"base\", blocksAttacks.itemDamage().base());\n            itemDamageTag.putFloat(\"factor\", blocksAttacks.itemDamage().factor());\n            if (blocksAttacks.bypassedBy() != null) {\n                itemDamageTag.putString(\"bypassed_by\", blocksAttacks.bypassedBy().tagKey());\n            }\n            if (blocksAttacks.blockSound() != null) {\n                blocksAttackTag.put(\"block_sound\", holderToTag(blocksAttacks.blockSound(), this::saveSoundEvent));\n            }\n            if (blocksAttacks.disableSound() != null) {\n                blocksAttackTag.put(\"disable_sound\", holderToTag(blocksAttacks.disableSound(), this::saveSoundEvent));\n            }\n        }\n\n        final TooltipDisplay tooltipDisplay = dataContainer.get(StructuredDataKey.TOOLTIP_DISPLAY);\n        if (tooltipDisplay != null) {\n            backupTag.put(\"hidden_components\", new IntArrayTag(tooltipDisplay.hiddenComponents().toIntArray()));\n        }\n\n        final TropicalFishPattern tropicalFishPattern = dataContainer.get(StructuredDataKey.TROPICAL_FISH_PATTERN);\n        if (tropicalFishPattern != null) {\n            backupTag.putInt(\"tropical_fish_pattern\", tropicalFishPattern.packedId());\n        }\n\n        saveKeyData(StructuredDataKey.PROVIDES_BANNER_PATTERNS1_21_5, dataContainer, backupTag);\n        saveFloatData(StructuredDataKey.POTION_DURATION_SCALE, dataContainer, backupTag);\n        saveIntData(StructuredDataKey.VILLAGER_VARIANT, dataContainer, backupTag);\n        saveIntData(StructuredDataKey.FOX_VARIANT, dataContainer, backupTag);\n        saveIntData(StructuredDataKey.SALMON_SIZE, dataContainer, backupTag);\n        saveIntData(StructuredDataKey.PARROT_VARIANT, dataContainer, backupTag);\n        saveIntData(StructuredDataKey.TROPICAL_FISH_BASE_COLOR, dataContainer, backupTag);\n        saveIntData(StructuredDataKey.TROPICAL_FISH_PATTERN_COLOR, dataContainer, backupTag);\n        saveIntData(StructuredDataKey.MOOSHROOM_VARIANT, dataContainer, backupTag);\n        saveIntData(StructuredDataKey.RABBIT_VARIANT, dataContainer, backupTag);\n        saveIntData(StructuredDataKey.FROG_VARIANT, dataContainer, backupTag);\n        saveIntData(StructuredDataKey.HORSE_VARIANT, dataContainer, backupTag);\n        saveIntData(StructuredDataKey.LLAMA_VARIANT, dataContainer, backupTag);\n        saveIntData(StructuredDataKey.AXOLOTL_VARIANT, dataContainer, backupTag);\n        saveIntData(StructuredDataKey.CAT_VARIANT, dataContainer, backupTag);\n        saveIntData(StructuredDataKey.CAT_COLLAR, dataContainer, backupTag);\n        saveIntData(StructuredDataKey.SHEEP_COLOR, dataContainer, backupTag);\n        saveIntData(StructuredDataKey.SHULKER_COLOR, dataContainer, backupTag);\n        saveIntData(StructuredDataKey.WOLF_SOUND_VARIANT, dataContainer, backupTag);\n        saveIntData(StructuredDataKey.COW_VARIANT, dataContainer, backupTag);\n        saveIntData(StructuredDataKey.PIG_VARIANT, dataContainer, backupTag);\n        saveIntData(StructuredDataKey.WOLF_VARIANT, dataContainer, backupTag);\n\n        final Either<Integer, String> chickenVariant = dataContainer.get(StructuredDataKey.CHICKEN_VARIANT1_21_5);\n        if (chickenVariant != null) {\n            if (chickenVariant.isLeft()) {\n                backupTag.putInt(\"chicken_variant\", chickenVariant.left());\n            } else {\n                backupTag.putString(\"chicken_variant\", chickenVariant.right());\n            }\n        }\n\n        saveHolderData(StructuredDataKey.PAINTING_VARIANT, dataContainer, backupTag, (paintingVariant, tag) -> {\n            tag.putInt(\"width\", paintingVariant.width());\n            tag.putInt(\"height\", paintingVariant.height());\n            tag.putString(\"asset_id\", paintingVariant.assetId());\n            if (paintingVariant.title() != null) {\n                tag.put(\"title\", paintingVariant.title());\n            }\n            if (paintingVariant.author() != null) {\n                tag.put(\"author\", paintingVariant.author());\n            }\n        });\n\n        saveHolderData(StructuredDataKey.BREAK_SOUND, dataContainer, backupTag, this::saveSoundEvent);\n    }\n\n    @Override\n    protected void handleItemDataComponentsToClient(final UserConnection connection, final Item item, final StructuredDataContainer dataContainer) {\n        super.handleItemDataComponentsToClient(connection, item, dataContainer);\n        downgradeItemData(item);\n    }\n\n    @Override\n    protected void handleItemDataComponentsToServer(final UserConnection connection, final Item item, final StructuredDataContainer container) {\n        super.handleItemDataComponentsToServer(connection, item, container);\n        updateItemData(item);\n    }\n\n    @Override\n    protected void restoreBackupData(final Item item, final StructuredDataContainer data, final CompoundTag customData) {\n        super.restoreBackupData(item, data, customData);\n        if (!(customData.remove(nbtTagName(\"backup\")) instanceof final CompoundTag backupTag)) {\n            return;\n        }\n\n        final IntArrayTag hiddenComponentsTag = backupTag.getIntArrayTag(\"hidden_components\");\n        if (hiddenComponentsTag != null) {\n            data.set(StructuredDataKey.TOOLTIP_DISPLAY, new TooltipDisplay(data.has(StructuredDataKey.HIDE_TOOLTIP), new IntLinkedOpenHashSet(hiddenComponentsTag.getValue())));\n        }\n\n        if (backupTag.getBoolean(\"tool\")) {\n            data.replace(StructuredDataKey.TOOL1_20_5, StructuredDataKey.TOOL1_21_5, t -> new ToolProperties(t.rules(), t.defaultMiningSpeed(), t.damagePerBlock(), true));\n        }\n\n        if (backupTag.getBoolean(\"equippable\")) {\n            data.replace(StructuredDataKey.EQUIPPABLE1_21_2, StructuredDataKey.EQUIPPABLE1_21_5, e -> new Equippable(e.equipmentSlot(), e.soundEvent(), e.model(), e.cameraOverlay(), e.allowedEntities(), e.dispensable(), e.swappable(), e.damageOnHurt(), true));\n        }\n\n        final CompoundTag weaponTag = backupTag.getCompoundTag(\"weapon\");\n        if (weaponTag != null) {\n            data.set(StructuredDataKey.WEAPON, new Weapon(weaponTag.getInt(\"item_damage_per_attack\"), weaponTag.getFloat(\"disable_blocking_for_seconds\")));\n        }\n\n        final Tag materialTag = backupTag.get(\"provides_trim_material\");\n        if (materialTag != null) {\n            data.set(StructuredDataKey.PROVIDES_TRIM_MATERIAL1_21_5, new ProvidesTrimMaterial(restoreEitherHolder(backupTag, \"provides_trim_material\", tag -> {\n                final String assetName = tag.getString(\"asset_name\");\n                final int itemId = tag.getInt(\"item_id\");\n                final float itemModelIndex = tag.getFloat(\"item_model_index\");\n                final CompoundTag overrideArmorMaterialsTag = tag.getCompoundTag(\"override_armor_materials\");\n                final Map<String, String> overrideArmorMaterials = new HashMap<>();\n                for (final String key : overrideArmorMaterialsTag.keySet()) {\n                    overrideArmorMaterials.put(key, overrideArmorMaterialsTag.getString(key));\n                }\n                final Tag description = tag.get(\"description\");\n                return new ArmorTrimMaterial(assetName, itemId, itemModelIndex, overrideArmorMaterials, description);\n            })));\n        }\n\n        final CompoundTag blocksAttackTag = backupTag.getCompoundTag(\"blocks_attack\");\n        if (blocksAttackTag != null) {\n            final float blockDelaySeconds = blocksAttackTag.getFloat(\"block_delay_seconds\");\n            final float disableCooldownScale = blocksAttackTag.getFloat(\"disable_cooldown_scale\");\n            final CompoundTag itemDamageTag = blocksAttackTag.getCompoundTag(\"item_damage\");\n            final ItemDamageFunction itemDamage = new ItemDamageFunction(itemDamageTag.getFloat(\"threshold\"), itemDamageTag.getFloat(\"base\"), itemDamageTag.getFloat(\"factor\"));\n            final String bypassedBy = blocksAttackTag.getString(\"bypassed_by\");\n            final Holder<SoundEvent> blockSound = blocksAttackTag.contains(\"block_sound\") ? restoreHolder(blocksAttackTag, \"block_sound\", this::tagToSound) : null;\n            final Holder<SoundEvent> disableSound = blocksAttackTag.contains(\"disable_sound\") ? restoreHolder(blocksAttackTag, \"disable_sound\", this::tagToSound) : null;\n\n            final List<DamageReduction> damageReductions = new ArrayList<>();\n            for (final CompoundTag damageReductionTag : blocksAttackTag.getListTag(\"damage_reductions\", CompoundTag.class)) {\n                final float horizontalBlockingAngle = damageReductionTag.getFloat(\"horizontal_blocking_angle\");\n                final HolderSet type = damageReductionTag.contains(\"type\") ? restoreHolderSet(damageReductionTag, \"type\") : null;\n                final float base = damageReductionTag.getFloat(\"base\");\n                final float factor = damageReductionTag.getFloat(\"factor\");\n                damageReductions.add(new DamageReduction(horizontalBlockingAngle, type, base, factor));\n            }\n\n            data.set(StructuredDataKey.BLOCKS_ATTACKS1_21_5, new BlocksAttacks(\n                blockDelaySeconds, disableCooldownScale, damageReductions.toArray(new DamageReduction[0]),\n                itemDamage, bypassedBy != null ? HolderSet.of(bypassedBy) : null, blockSound, disableSound\n            ));\n        }\n\n        final IntTag chickenVariant = backupTag.getIntTag(\"chicken_variant\");\n        if (chickenVariant != null) {\n            data.set(StructuredDataKey.CHICKEN_VARIANT1_21_5, Either.left(chickenVariant.asInt()));\n        } else {\n            final String chickenVariantKey = backupTag.getString(\"chicken_variant\");\n            if (chickenVariantKey != null) {\n                data.set(StructuredDataKey.CHICKEN_VARIANT1_21_5, Either.right(chickenVariantKey));\n            }\n        }\n\n        final IntTag tropicalFishPattern = backupTag.getIntTag(\"tropical_fish_pattern\");\n        if (tropicalFishPattern != null) {\n            data.set(StructuredDataKey.TROPICAL_FISH_PATTERN, new TropicalFishPattern(tropicalFishPattern.asInt()));\n        }\n\n        restoreKeyData(StructuredDataKey.PROVIDES_BANNER_PATTERNS1_21_5, data, backupTag);\n        restoreFloatData(StructuredDataKey.POTION_DURATION_SCALE, data, backupTag);\n        restoreIntData(StructuredDataKey.VILLAGER_VARIANT, data, backupTag);\n        restoreIntData(StructuredDataKey.FOX_VARIANT, data, backupTag);\n        restoreIntData(StructuredDataKey.SALMON_SIZE, data, backupTag);\n        restoreIntData(StructuredDataKey.PARROT_VARIANT, data, backupTag);\n        restoreIntData(StructuredDataKey.TROPICAL_FISH_BASE_COLOR, data, backupTag);\n        restoreIntData(StructuredDataKey.TROPICAL_FISH_PATTERN_COLOR, data, backupTag);\n        restoreIntData(StructuredDataKey.MOOSHROOM_VARIANT, data, backupTag);\n        restoreIntData(StructuredDataKey.RABBIT_VARIANT, data, backupTag);\n        restoreIntData(StructuredDataKey.FROG_VARIANT, data, backupTag);\n        restoreIntData(StructuredDataKey.HORSE_VARIANT, data, backupTag);\n        restoreIntData(StructuredDataKey.LLAMA_VARIANT, data, backupTag);\n        restoreIntData(StructuredDataKey.AXOLOTL_VARIANT, data, backupTag);\n        restoreIntData(StructuredDataKey.CAT_VARIANT, data, backupTag);\n        restoreIntData(StructuredDataKey.CAT_COLLAR, data, backupTag);\n        restoreIntData(StructuredDataKey.SHEEP_COLOR, data, backupTag);\n        restoreIntData(StructuredDataKey.SHULKER_COLOR, data, backupTag);\n        restoreIntData(StructuredDataKey.WOLF_SOUND_VARIANT, data, backupTag);\n        restoreIntData(StructuredDataKey.COW_VARIANT, data, backupTag);\n        restoreIntData(StructuredDataKey.PIG_VARIANT, data, backupTag);\n        restoreIntData(StructuredDataKey.WOLF_VARIANT, data, backupTag);\n\n        restoreHolderData(StructuredDataKey.BREAK_SOUND, data, backupTag, this::tagToSound);\n        restoreHolderData(StructuredDataKey.PAINTING_VARIANT, data, backupTag, tag -> {\n            final int width = tag.getInt(\"width\");\n            final int height = tag.getInt(\"height\");\n            final String assetId = tag.getString(\"asset_id\");\n            final Tag title = tag.get(\"title\");\n            final Tag author = tag.get(\"author\");\n            return new PaintingVariant(width, height, assetId, title, author);\n        });\n\n        removeCustomTag(data, customData);\n    }\n\n    private SoundEvent tagToSound(final CompoundTag tag) {\n        final String identifier = tag.getString(\"identifier\");\n        final FloatTag fixedRangeTag = tag.getFloatTag(\"fixed_range\");\n        return new SoundEvent(identifier, fixedRangeTag != null ? fixedRangeTag.asFloat() : null);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_5to1_21_4/rewriter/BlockPacketRewriter1_21_5.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_5to1_21_4.rewriter;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.ListTag;\nimport com.viaversion.nbt.tag.Tag;\nimport com.viaversion.viabackwards.protocol.v1_21_5to1_21_4.Protocol1_21_5To1_21_4;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.blockentity.BlockEntity;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.chunk.ChunkType1_20_2;\nimport com.viaversion.viaversion.api.type.types.chunk.ChunkType1_21_5;\nimport com.viaversion.viaversion.protocols.v1_21_4to1_21_5.packet.ClientboundPacket1_21_5;\nimport com.viaversion.viaversion.rewriter.BlockRewriter;\n\npublic final class BlockPacketRewriter1_21_5 extends BlockRewriter<ClientboundPacket1_21_5> {\n\n    private static final int SIGN_BOCK_ENTITY_ID = 7;\n    private static final int HANGING_SIGN_BOCK_ENTITY_ID = 8;\n    private final Protocol1_21_5To1_21_4 protocol;\n\n    public BlockPacketRewriter1_21_5(final Protocol1_21_5To1_21_4 protocol) {\n        super(protocol, Types.BLOCK_POSITION1_14, Types.COMPOUND_TAG, ChunkType1_21_5::new, ChunkType1_20_2::new);\n        this.protocol = protocol;\n    }\n\n    @Override\n    public void handleBlockEntity(final UserConnection connection, final BlockEntity blockEntity) {\n        final CompoundTag tag = blockEntity.tag();\n        if (tag == null) {\n            return;\n        }\n\n        if (blockEntity.typeId() == SIGN_BOCK_ENTITY_ID || blockEntity.typeId() == HANGING_SIGN_BOCK_ENTITY_ID) {\n            updateSignMessages(connection, tag.getCompoundTag(\"front_text\"));\n            updateSignMessages(connection, tag.getCompoundTag(\"back_text\"));\n        }\n\n        final Tag customName = tag.get(\"CustomName\");\n        if (customName != null) {\n            tag.putString(\"CustomName\", protocol.getComponentRewriter().toUglyJson(connection, customName));\n        }\n    }\n\n    private void updateSignMessages(final UserConnection connection, final CompoundTag tag) {\n        if (tag == null) {\n            return;\n        }\n\n        final ListTag<?> messages = tag.getListTag(\"messages\");\n        tag.put(\"messages\", protocol.getComponentRewriter().updateComponentList(connection, messages));\n\n        final ListTag<?> filteredMessages = tag.getListTag(\"filtered_messages\");\n        if (filteredMessages != null) {\n            tag.put(\"filtered_messages\", protocol.getComponentRewriter().updateComponentList(connection, filteredMessages));\n        }\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_5to1_21_4/rewriter/ComponentRewriter1_21_5.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_5to1_21_4.rewriter;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.ListTag;\nimport com.viaversion.nbt.tag.NumberTag;\nimport com.viaversion.nbt.tag.StringTag;\nimport com.viaversion.nbt.tag.Tag;\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viabackwards.api.rewriters.text.NBTComponentRewriter;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.data.StructuredDataKey;\nimport com.viaversion.viaversion.protocols.v1_21_4to1_21_5.packet.ClientboundPacket1_21_5;\nimport com.viaversion.viaversion.protocols.v1_21_4to1_21_5.rewriter.BlockItemPacketRewriter1_21_5;\nimport com.viaversion.viaversion.util.Key;\nimport com.viaversion.viaversion.util.SerializerVersion;\nimport com.viaversion.viaversion.util.TagUtil;\nimport java.util.HashSet;\nimport java.util.Set;\nimport org.checkerframework.checker.nullness.qual.Nullable;\n\nimport static com.viaversion.viaversion.util.TagUtil.getNamespacedCompoundTag;\nimport static com.viaversion.viaversion.util.TagUtil.getNamespacedCompoundTagList;\nimport static com.viaversion.viaversion.util.TagUtil.getNamespacedNumberTag;\nimport static com.viaversion.viaversion.util.TagUtil.removeNamespaced;\n\npublic final class ComponentRewriter1_21_5 extends NBTComponentRewriter<ClientboundPacket1_21_5> {\n\n    public ComponentRewriter1_21_5(final BackwardsProtocol<ClientboundPacket1_21_5, ?, ?, ?> protocol) {\n        super(protocol);\n    }\n\n    @Override\n    protected void processCompoundTag(final UserConnection connection, final CompoundTag tag) {\n        super.processCompoundTag(connection, tag);\n        if (tag.remove(\"hover_event\") instanceof final CompoundTag hoverEvent) {\n            tag.put(\"hoverEvent\", hoverEvent);\n        }\n\n        if (tag.remove(\"click_event\") instanceof final CompoundTag clickEvent) {\n            tag.put(\"clickEvent\", clickEvent);\n            updateClickEvent(clickEvent);\n        }\n    }\n\n    @Override\n    protected void handleHoverEvent(final UserConnection connection, final CompoundTag hoverEventTag) {\n        final String action = hoverEventTag.getString(\"action\");\n        if (action == null) {\n            return;\n        }\n\n        switch (action) {\n            case \"show_text\" -> updateShowTextHover(hoverEventTag);\n            case \"show_entity\" -> updateShowEntityHover(hoverEventTag);\n            case \"show_item\" -> updateShowItemHover(connection, hoverEventTag);\n        }\n    }\n\n    private void updateClickEvent(final CompoundTag clickEventTag) {\n        final String action = clickEventTag.getString(\"action\");\n        if (action == null) {\n            return;\n        }\n\n        switch (action) {\n            case \"open_url\" -> clickEventTag.put(\"value\", clickEventTag.getStringTag(\"url\"));\n            case \"change_page\" -> clickEventTag.putString(\"value\", Integer.toString(clickEventTag.getInt(\"page\")));\n            case \"run_command\" -> {\n                final StringTag command = clickEventTag.getStringTag(\"command\");\n                if (command != null && !command.getValue().startsWith(\"/\")) {\n                    command.setValue(\"/\" + command.getValue());\n                }\n                clickEventTag.put(\"value\", command);\n            }\n            case \"suggest_command\" -> clickEventTag.put(\"value\", clickEventTag.getStringTag(\"command\"));\n        }\n    }\n\n    private void updateShowTextHover(final CompoundTag hoverEventTag) {\n        final Tag text = hoverEventTag.remove(\"value\");\n        hoverEventTag.put(\"contents\", text);\n    }\n\n    private void updateShowItemHover(final UserConnection connection, final CompoundTag hoverEventTag) {\n        final CompoundTag contents = new CompoundTag();\n        hoverEventTag.put(\"contents\", contents);\n\n        if (hoverEventTag.get(\"count\") instanceof NumberTag countTag) {\n            contents.put(\"count\", countTag);\n        }\n        if (hoverEventTag.get(\"id\") instanceof StringTag idTag) {\n            contents.put(\"id\", idTag);\n        }\n\n        final CompoundTag componentsTag = hoverEventTag.getCompoundTag(\"components\");\n        handleShowItem(connection, contents, componentsTag);\n        if (componentsTag != null) {\n            hoverEventTag.remove(\"components\");\n            contents.put(\"components\", componentsTag);\n        }\n    }\n\n    @Override\n    protected void handleShowItem(final UserConnection connection, final CompoundTag itemTag, @Nullable final CompoundTag componentsTag) {\n        super.handleShowItem(connection, itemTag, componentsTag);\n        if (componentsTag == null) {\n            return;\n        }\n\n        insertUglyJson(componentsTag, connection);\n        updateDataComponents(componentsTag);\n\n        removeDataComponents(componentsTag, BlockItemPacketRewriter1_21_5.NEW_DATA_TO_REMOVE);\n    }\n\n    private void updateDataComponents(final CompoundTag componentsTag) {\n        final CompoundTag tooltipDisplay = getNamespacedCompoundTag(componentsTag, \"tooltip_display\");\n        Set<String> hiddenComponents = Set.of();\n        if (tooltipDisplay != null) {\n            final ListTag<StringTag> hiddenComponentsTag = tooltipDisplay.getListTag(\"hidden_components\", StringTag.class);\n            if (hiddenComponentsTag != null) {\n                hiddenComponents = new HashSet<>(hiddenComponentsTag.size());\n                for (final StringTag stringTag : hiddenComponentsTag) {\n                    hiddenComponents.add(Key.stripMinecraftNamespace(stringTag.getValue()));\n                }\n            }\n        }\n        if (hiddenComponents.containsAll(BlockItemPacketRewriter1_21_5.HIDE_ADDITIONAL_KEYS.stream().map(StructuredDataKey::identifier).toList())) {\n            componentsTag.put(\"hide_additional_tooltip\", new CompoundTag());\n        }\n\n        final ListTag<CompoundTag> attributeModifiers = getNamespacedCompoundTagList(componentsTag, \"attribute_modifiers\");\n        if (attributeModifiers != null) {\n            removeNamespaced(componentsTag, \"attribute_modifiers\");\n            final CompoundTag attributesParent = new CompoundTag();\n            attributesParent.put(\"modifiers\", attributeModifiers);\n            attributesParent.putBoolean(\"show_in_tooltip\", hiddenComponents.contains(\"attribute_modifiers\"));\n            componentsTag.put(\"attribute_modifiers\", attributesParent);\n        }\n\n        final NumberTag dyedColor = getNamespacedNumberTag(componentsTag, \"dyed_color\");\n        if (dyedColor != null) {\n            removeNamespaced(componentsTag, \"dyed_color\");\n            final CompoundTag dyedColorParent = new CompoundTag();\n            dyedColorParent.put(\"rgb\", dyedColor);\n            dyedColorParent.putBoolean(\"show_in_tooltip\", hiddenComponents.contains(\"dyed_color\"));\n            componentsTag.put(\"dyed_color\", dyedColorParent);\n        }\n\n        updateShowInTooltip(componentsTag, \"unbreakable\", hiddenComponents);\n        updateShowInTooltip(componentsTag, \"dyed_color\", hiddenComponents);\n        updateShowInTooltip(componentsTag, \"trim\", hiddenComponents);\n        updateShowInTooltip(componentsTag, \"jukebox_playable\", hiddenComponents);\n        handleAdventureModePredicate(componentsTag, \"can_place_on\", hiddenComponents);\n        handleAdventureModePredicate(componentsTag, \"can_break\", hiddenComponents);\n        handleEnchantments(componentsTag, \"enchantments\", hiddenComponents);\n        handleEnchantments(componentsTag, \"stored_enchantments\", hiddenComponents);\n\n        removeDataComponents(componentsTag, StructuredDataKey.INSTRUMENT1_21_5, StructuredDataKey.JUKEBOX_PLAYABLE1_21_5);\n    }\n\n    private void updateShowInTooltip(final CompoundTag tag, final String key, final Set<String> hiddenComponents) {\n        final CompoundTag data = getNamespacedCompoundTag(tag, key);\n        if (data != null) {\n            data.putBoolean(\"show_in_tooltip\", !hiddenComponents.contains(key));\n        }\n    }\n\n    private void handleAdventureModePredicate(final CompoundTag componentsTag, final String key, final Set<String> hiddenComponents) {\n        final ListTag<CompoundTag> blockPredicates = getNamespacedCompoundTagList(componentsTag, key);\n        if (blockPredicates == null) {\n            return;\n        }\n\n        removeDataComponents(componentsTag, key);\n        final CompoundTag predicate = new CompoundTag();\n        predicate.put(\"predicates\", blockPredicates);\n        predicate.putBoolean(\"show_in_tooltip\", !hiddenComponents.contains(key));\n        componentsTag.put(key, predicate);\n    }\n\n    private void handleEnchantments(final CompoundTag componentsTag, final String key, final Set<String> hiddenComponents) {\n        final CompoundTag levels = getNamespacedCompoundTag(componentsTag, key);\n        if (levels != null) {\n            removeNamespaced(componentsTag, key);\n            final CompoundTag enchantments = new CompoundTag();\n            enchantments.put(\"levels\", levels);\n            enchantments.putBoolean(\"show_in_tooltip\", !hiddenComponents.contains(key));\n            componentsTag.put(key, enchantments);\n        }\n    }\n\n    private void insertUglyJson(final CompoundTag componentsTag, final UserConnection connection) {\n        insertUglyJson(componentsTag, \"item_name\", connection);\n        insertUglyJson(componentsTag, \"custom_name\", connection);\n\n        final String loreKey = TagUtil.getNamespacedTagKey(componentsTag, \"lore\");\n        final ListTag<?> lore = componentsTag.getListTag(loreKey);\n        if (lore != null) {\n            componentsTag.put(loreKey, updateComponentList(connection, lore));\n        }\n    }\n\n    public ListTag<StringTag> updateComponentList(final UserConnection connection, final ListTag<?> messages) {\n        final ListTag<StringTag> updatedMessages = new ListTag<>(StringTag.class);\n        for (final Tag message : messages) {\n            updatedMessages.add(new StringTag(toUglyJson(connection, message)));\n        }\n        return updatedMessages;\n    }\n\n    private void insertUglyJson(final CompoundTag componentsTag, final String key, final UserConnection connection) {\n        final String actualKey = TagUtil.getNamespacedTagKey(componentsTag, key);\n        final Tag tag = componentsTag.get(actualKey);\n        if (tag == null) {\n            return;\n        }\n\n        componentsTag.putString(actualKey, toUglyJson(connection, tag));\n    }\n\n    private void updateShowEntityHover(final CompoundTag hoverEventTag) {\n        final CompoundTag contents = new CompoundTag();\n        hoverEventTag.put(\"contents\", contents);\n\n        final Tag nameTag = hoverEventTag.remove(\"name\");\n        if (nameTag != null) {\n            contents.put(\"name\", nameTag);\n        }\n\n        if (hoverEventTag.remove(\"id\") instanceof StringTag idTag) {\n            idTag.setValue(protocol.getEntityRewriter().mappedEntityIdentifier(idTag.getValue()));\n            contents.put(\"type\", idTag);\n        }\n\n        final Tag uuidTag = hoverEventTag.remove(\"uuid\");\n        if (uuidTag != null) {\n            contents.put(\"id\", uuidTag);\n        }\n    }\n\n    String toUglyJson(final UserConnection connection, final Tag value) {\n        processTag(connection, value);\n        return SerializerVersion.V1_21_4.toString(SerializerVersion.V1_21_4.toComponent(value));\n    }\n\n    @Override\n    protected void handleWrittenBookContents(final UserConnection connection, final CompoundTag tag) {\n        final CompoundTag book = TagUtil.getNamespacedCompoundTag(tag, \"written_book_content\");\n        if (book == null) {\n            return;\n        }\n\n        final ListTag<CompoundTag> pagesTag = book.getListTag(\"pages\", CompoundTag.class);\n        if (pagesTag == null) {\n            return;\n        }\n\n        for (final CompoundTag compoundTag : pagesTag) {\n            final Tag raw = compoundTag.get(\"raw\");\n            compoundTag.putString(\"raw\", toUglyJson(connection, raw));\n\n            final Tag filtered = compoundTag.get(\"filtered\");\n            if (filtered != null) {\n                compoundTag.putString(\"filtered\", toUglyJson(connection, raw));\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_5to1_21_4/rewriter/EntityPacketRewriter1_21_5.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_5to1_21_4.rewriter;\n\nimport com.viaversion.viabackwards.api.rewriters.EntityRewriter;\nimport com.viaversion.viabackwards.protocol.v1_21_5to1_21_4.Protocol1_21_5To1_21_4;\nimport com.viaversion.viabackwards.protocol.v1_21_5to1_21_4.storage.HorseDataStorage;\nimport com.viaversion.viaversion.api.data.entity.TrackedEntity;\nimport com.viaversion.viaversion.api.minecraft.Holder;\nimport com.viaversion.viaversion.api.minecraft.WolfVariant;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityType;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_21_5;\nimport com.viaversion.viaversion.api.minecraft.entitydata.EntityData;\nimport com.viaversion.viaversion.api.minecraft.entitydata.types.EntityDataTypes1_21_2;\nimport com.viaversion.viaversion.api.minecraft.entitydata.types.EntityDataTypes1_21_5;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.version.VersionedTypes;\nimport com.viaversion.viaversion.protocols.v1_21_4to1_21_5.packet.ClientboundPacket1_21_5;\nimport com.viaversion.viaversion.protocols.v1_21_4to1_21_5.packet.ClientboundPackets1_21_5;\nimport com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ClientboundPackets1_21_2;\nimport java.util.UUID;\n\npublic final class EntityPacketRewriter1_21_5 extends EntityRewriter<ClientboundPacket1_21_5, Protocol1_21_5To1_21_4> {\n\n    public EntityPacketRewriter1_21_5(final Protocol1_21_5To1_21_4 protocol) {\n        super(protocol, protocol.mappedTypes().entityDataTypes().optionalComponentType, protocol.mappedTypes().entityDataTypes().booleanType);\n    }\n\n    @Override\n    public void registerPackets() {\n        protocol.replaceClientbound(ClientboundPackets1_21_5.ADD_ENTITY, wrapper -> {\n            final int entityId = wrapper.passthrough(Types.VAR_INT);\n\n            final UUID uuid = wrapper.read(Types.UUID);\n            final int entityType = wrapper.read(Types.VAR_INT);\n            if (entityType != EntityTypes1_21_5.EXPERIENCE_ORB.getId()) {\n                wrapper.write(Types.UUID, uuid);\n                wrapper.write(Types.VAR_INT, entityType);\n                wrapper.passthrough(Types.DOUBLE); // X\n                wrapper.passthrough(Types.DOUBLE); // Y\n                wrapper.passthrough(Types.DOUBLE); // Z\n                wrapper.passthrough(Types.BYTE); // Pitch\n                wrapper.passthrough(Types.BYTE); // Yaw\n                wrapper.passthrough(Types.BYTE); // Head yaw\n                wrapper.passthrough(Types.VAR_INT); // Data\n                getSpawnTrackerWithDataHandler1_19().handle(wrapper);\n                return;\n            }\n\n            tracker(wrapper.user()).addEntity(entityId, EntityTypes1_21_5.EXPERIENCE_ORB);\n\n            // Back to its own special packet\n            wrapper.setPacketType(ClientboundPackets1_21_2.ADD_EXPERIENCE_ORB);\n            wrapper.passthrough(Types.DOUBLE); // X\n            wrapper.passthrough(Types.DOUBLE); // Y\n            wrapper.passthrough(Types.DOUBLE); // Z\n            wrapper.read(Types.BYTE); // Pitch\n            wrapper.read(Types.BYTE); // Yaw\n            wrapper.read(Types.BYTE); // Head yaw\n\n            final int data = wrapper.read(Types.VAR_INT);\n            wrapper.write(Types.SHORT, (short) data);\n\n            final short velocityX = wrapper.read(Types.SHORT);\n            final short velocityY = wrapper.read(Types.SHORT);\n            final short velocityZ = wrapper.read(Types.SHORT);\n            if (velocityX != 0 || velocityY != 0 || velocityZ != 0) {\n                // Send movement separately\n                final PacketWrapper motionPacket = wrapper.create(ClientboundPackets1_21_2.SET_ENTITY_MOTION);\n                motionPacket.write(Types.VAR_INT, entityId);\n                motionPacket.write(Types.SHORT, velocityX);\n                motionPacket.write(Types.SHORT, velocityY);\n                motionPacket.write(Types.SHORT, velocityZ);\n                wrapper.send(Protocol1_21_5To1_21_4.class);\n                motionPacket.send(Protocol1_21_5To1_21_4.class);\n                wrapper.cancel();\n            }\n        });\n\n        protocol.replaceClientbound(ClientboundPackets1_21_5.SET_PLAYER_TEAM, wrapper -> {\n            wrapper.passthrough(Types.STRING); // Team Name\n            final byte action = wrapper.passthrough(Types.BYTE); // Mode\n            if (action == 0 || action == 2) {\n                protocol.getComponentRewriter().passthroughAndProcess(wrapper); // Display name\n                wrapper.passthrough(Types.BYTE); // Flags\n\n                final int nametagVisibility = wrapper.read(Types.VAR_INT);\n                final int collisionRule = wrapper.read(Types.VAR_INT);\n                wrapper.write(Types.STRING, visibility(nametagVisibility));\n                wrapper.write(Types.STRING, collision(collisionRule));\n\n                wrapper.passthrough(Types.VAR_INT); // Color\n                protocol.getComponentRewriter().passthroughAndProcess(wrapper); // Prefix\n                protocol.getComponentRewriter().passthroughAndProcess(wrapper); // Suffix\n            }\n        });\n    }\n\n    private String visibility(final int id) {\n        return switch (id) {\n            case 0 -> \"always\";\n            case 1 -> \"never\";\n            case 2 -> \"hideForOtherTeams\";\n            case 3 -> \"hideForOwnTeam\";\n            default -> \"always\";\n        };\n    }\n\n    private String collision(final int id) {\n        return switch (id) {\n            case 0 -> \"always\";\n            case 1 -> \"never\";\n            case 2 -> \"pushOtherTeams\";\n            case 3 -> \"pushOwnTeam\";\n            default -> \"always\";\n        };\n    }\n\n    @Override\n    protected void registerRewrites() {\n        final EntityDataTypes1_21_5 entityDataTypes = VersionedTypes.V1_21_5.entityDataTypes;\n        final EntityDataTypes1_21_2 mappedEntityDataTypes = VersionedTypes.V1_21_4.entityDataTypes;\n        dataTypeMapper()\n            .removed(entityDataTypes.cowVariantType)\n            .removed(entityDataTypes.wolfSoundVariantType)\n            .removed(entityDataTypes.pigVariantType)\n            .removed(entityDataTypes.chickenVariantType)\n            .skip(entityDataTypes.wolfVariantType)\n            .register();\n        filter().dataType(entityDataTypes.wolfVariantType).handler((event, data) -> {\n            final int type = data.value();\n            final Holder<WolfVariant> variant = Holder.of(type);\n            data.setTypeAndValue(mappedEntityDataTypes.wolfVariantType, variant);\n        });\n\n        registerEntityDataTypeHandler1_20_3(\n            mappedEntityDataTypes.itemType,\n            mappedEntityDataTypes.blockStateType,\n            mappedEntityDataTypes.optionalBlockStateType,\n            mappedEntityDataTypes.particleType,\n            mappedEntityDataTypes.particlesType,\n            mappedEntityDataTypes.componentType,\n            mappedEntityDataTypes.optionalComponentType\n        );\n\n        filter().dataType(mappedEntityDataTypes.frogVariantType).handler((event, data) -> {\n            final int value = data.value();\n            final String variantKey = protocol.getRegistryDataRewriter().getMappings(\"frog_variant\").idToKey(value);\n            final int newValue = (variantKey == null) ? 0 : switch (variantKey) {\n                case \"cold\" -> 2;\n                case \"temperate\" -> 0;\n                case \"warm\" -> 1;\n                default -> 0;\n            };\n            data.setValue(newValue);\n        });\n        filter().dataType(mappedEntityDataTypes.catVariantType).handler((event, data) -> {\n            final int value = data.value();\n            final String variantKey = protocol.getRegistryDataRewriter().getMappings(\"cat_variant\").idToKey(value);\n            final int newValue = (variantKey == null) ? 1 : switch (variantKey) {\n                case \"all_black\" -> 10;\n                case \"black\" -> 1;\n                case \"british_shorthair\" -> 4;\n                case \"calico\" -> 5;\n                case \"jellie\" -> 9;\n                case \"persian\" -> 6;\n                case \"ragdoll\" -> 7;\n                case \"red\" -> 2;\n                case \"siamese\" -> 3;\n                case \"tabby\" -> 0;\n                case \"white\" -> 8;\n                default -> 1;\n            };\n            data.setValue(newValue);\n        });\n\n        filter().type(EntityTypes1_21_5.ABSTRACT_MINECART).addIndex(13); // Custom display\n        filter().type(EntityTypes1_21_5.ABSTRACT_MINECART).index(11).handler((event, data) -> {\n            final int state = (int) data.getValue();\n            if (state == 0) {\n                event.cancel();\n                return;\n            }\n\n            final int mappedBlockState = protocol.getMappingData().getNewBlockStateId(state);\n            data.setTypeAndValue(entityDataTypes.varIntType, mappedBlockState);\n            event.createExtraData(new EntityData(13, mappedEntityDataTypes.booleanType, true));\n        });\n\n        filter().type(EntityTypes1_21_5.MOOSHROOM).index(17).handler(((event, data) -> {\n            final int typeId = data.value();\n            final String typeName = typeId == 0 ? \"red\" : \"brown\";\n            data.setTypeAndValue(entityDataTypes.stringType, typeName);\n        }));\n\n        filter().type(EntityTypes1_21_5.ABSTRACT_HORSE).index(17).handler((event, data) -> {\n            // Store data and set saddled flag if needed\n            final TrackedEntity entity = event.trackedEntity();\n            final byte horseData = data.value();\n            boolean saddled = false;\n\n            final HorseDataStorage horseDataStorage;\n            if (entity.hasData() && (horseDataStorage = entity.data().get(HorseDataStorage.class)) != null && horseDataStorage.saddled()) {\n                saddled = true;\n                data.setValue((byte) (horseData | BlockItemPacketRewriter1_21_5.SADDLED_FLAG));\n            }\n\n            entity.data().put(new HorseDataStorage(horseData, saddled));\n        });\n\n        filter().type(EntityTypes1_21_5.CHICKEN).cancel(17); // Chicken variant\n        filter().type(EntityTypes1_21_5.COW).cancel(17); // Cow variant\n        filter().type(EntityTypes1_21_5.PIG).cancel(18); // Pig variant\n        filter().type(EntityTypes1_21_5.WOLF).cancel(23); // Sound variant\n        filter().type(EntityTypes1_21_5.EXPERIENCE_ORB).cancel(8); // Value\n\n        filter().type(EntityTypes1_21_5.DOLPHIN).addIndex(17); // Treasure pos\n        filter().type(EntityTypes1_21_5.TURTLE).addIndex(17); // Home pos\n\n        // Saddled\n        filter().type(EntityTypes1_21_5.PIG).addIndex(17);\n        filter().type(EntityTypes1_21_5.STRIDER).addIndex(19);\n\n    }\n\n    @Override\n    public EntityType typeFromId(final int type) {\n        return EntityTypes1_21_5.getTypeFromId(type);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_5to1_21_4/rewriter/RegistryDataRewriter1_21_5.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_5to1_21_4.rewriter;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.ListTag;\nimport com.viaversion.nbt.tag.StringTag;\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viabackwards.api.rewriters.BackwardsRegistryRewriter;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.RegistryEntry;\nimport java.util.List;\n\npublic final class RegistryDataRewriter1_21_5 extends BackwardsRegistryRewriter {\n\n    public RegistryDataRewriter1_21_5(final BackwardsProtocol<?, ?, ?, ?> protocol) {\n        super(protocol);\n\n        final List<String> newRegistries = List.of(\"pig_variant\", \"cow_variant\", \"frog_variant\", \"cat_variant\",\n            \"chicken_variant\", \"test_environment\", \"test_instance\", \"wolf_sound_variant\");\n        for (final String registry : newRegistries) {\n            remove(registry);\n        }\n    }\n\n    @Override\n    public RegistryEntry[] handle(final UserConnection connection, final String key, final RegistryEntry[] entries) {\n        final boolean trimPatternRegistry = key.equals(\"trim_pattern\");\n        if (trimPatternRegistry || key.equals(\"trim_material\")) {\n            updateTrim(entries, trimPatternRegistry ? \"template_item\" : \"ingredient\");\n        } else if (key.equals(\"enchantment\")) {\n            updateEnchantment(entries);\n        } else if (key.equals(\"wolf_variant\")) {\n            for (final RegistryEntry entry : entries) {\n                if (entry.tag() == null) {\n                    continue;\n                }\n\n                final CompoundTag variant = (CompoundTag) entry.tag();\n                final CompoundTag assets = (CompoundTag) variant.remove(\"assets\");\n                variant.put(\"wild_texture\", assets.get(\"wild\"));\n                variant.put(\"tame_texture\", assets.get(\"tame\"));\n                variant.put(\"angry_texture\", assets.get(\"angry\"));\n                variant.put(\"biomes\", new ListTag<>(StringTag.class));\n            }\n        }\n        return super.handle(connection, key, entries);\n    }\n\n    private void updateTrim(final RegistryEntry[] entries, final String itemKey) {\n        for (final RegistryEntry entry : entries) {\n            if (entry.tag() == null) {\n                continue;\n            }\n\n            final CompoundTag tag = (CompoundTag) entry.tag();\n            tag.putString(itemKey, \"stone\"); // dummy ingredient\n        }\n    }\n\n    private void updateEnchantment(final RegistryEntry[] entries) {\n        for (final RegistryEntry entry : entries) {\n            if (entry.tag() == null) {\n                continue;\n            }\n\n            final CompoundTag enchantment = (CompoundTag) entry.tag();\n            final ListTag<StringTag> slots = enchantment.getListTag(\"slots\", StringTag.class);\n            if (slots != null) {\n                slots.getValue().removeIf(tag -> tag.getValue().equals(\"saddle\")); // Remove saddle slot\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_5to1_21_4/storage/HashedItemConverterStorage.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_5to1_21_4.storage;\n\nimport com.viaversion.viaversion.api.connection.StorableObject;\nimport com.viaversion.viaversion.api.minecraft.codec.CodecContext;\nimport com.viaversion.viaversion.api.minecraft.codec.CodecContext.RegistryAccess;\nimport com.viaversion.viaversion.api.minecraft.codec.hash.Hasher;\nimport com.viaversion.viaversion.api.protocol.Protocol;\nimport com.viaversion.viaversion.codec.CodecRegistryContext;\nimport com.viaversion.viaversion.codec.hash.HashFunction;\nimport com.viaversion.viaversion.codec.hash.HashOps;\n\npublic class HashedItemConverterStorage implements StorableObject {\n\n    private final Hasher hasher;\n\n    public HashedItemConverterStorage(final Protocol<?, ?, ?, ?> protocol) {\n        final RegistryAccess registryAccess = RegistryAccess.of(protocol);\n        final CodecContext context = new CodecRegistryContext(protocol, registryAccess, false);\n        this.hasher = new HashOps(context, HashFunction.CRC32C);\n    }\n\n    public Hasher hasher() {\n        return hasher; // reusable\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_5to1_21_4/storage/HorseDataStorage.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_5to1_21_4.storage;\n\npublic record HorseDataStorage(byte data, boolean saddled) {\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_6to1_21_5/Protocol1_21_6To1_21_5.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_6to1_21_5;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.Tag;\nimport com.viaversion.viabackwards.ViaBackwards;\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viabackwards.api.data.BackwardsMappingData;\nimport com.viaversion.viabackwards.api.rewriters.BackwardsRegistryRewriter;\nimport com.viaversion.viabackwards.api.rewriters.text.NBTComponentRewriter;\nimport com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.data.Dialog;\nimport com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.provider.ChestDialogViewProvider;\nimport com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.provider.DialogViewProvider;\nimport com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.rewriter.BlockItemPacketRewriter1_21_6;\nimport com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.rewriter.ComponentRewriter1_21_6;\nimport com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.rewriter.EntityPacketRewriter1_21_6;\nimport com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.rewriter.RegistryDataRewriter1_21_6;\nimport com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.storage.ChestDialogStorage;\nimport com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.storage.ClickEvents;\nimport com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.storage.RegistryAndTags;\nimport com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.storage.ServerLinks;\nimport com.viaversion.viaversion.api.Via;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.Holder;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_21_6;\nimport com.viaversion.viaversion.api.platform.providers.ViaProviders;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.protocol.packet.provider.PacketTypesProvider;\nimport com.viaversion.viaversion.api.protocol.packet.provider.SimplePacketTypesProvider;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.chunk.ChunkType1_21_5;\nimport com.viaversion.viaversion.api.type.types.version.VersionedTypes;\nimport com.viaversion.viaversion.api.type.types.version.VersionedTypesHolder;\nimport com.viaversion.viaversion.data.entity.EntityTrackerBase;\nimport com.viaversion.viaversion.data.item.ItemHasherBase;\nimport com.viaversion.viaversion.protocols.v1_19_3to1_19_4.rewriter.CommandRewriter1_19_4;\nimport com.viaversion.viaversion.protocols.v1_20_3to1_20_5.packet.ServerboundConfigurationPackets1_20_5;\nimport com.viaversion.viaversion.protocols.v1_20_5to1_21.packet.ClientboundConfigurationPackets1_21;\nimport com.viaversion.viaversion.protocols.v1_21_4to1_21_5.packet.ClientboundPacket1_21_5;\nimport com.viaversion.viaversion.protocols.v1_21_4to1_21_5.packet.ClientboundPackets1_21_5;\nimport com.viaversion.viaversion.protocols.v1_21_4to1_21_5.packet.ServerboundPacket1_21_5;\nimport com.viaversion.viaversion.protocols.v1_21_4to1_21_5.packet.ServerboundPackets1_21_5;\nimport com.viaversion.viaversion.protocols.v1_21_4to1_21_5.rewriter.RecipeDisplayRewriter1_21_5;\nimport com.viaversion.viaversion.protocols.v1_21_5to1_21_6.Protocol1_21_5To1_21_6;\nimport com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ClientboundConfigurationPackets1_21_6;\nimport com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ClientboundPacket1_21_6;\nimport com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ClientboundPackets1_21_6;\nimport com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ServerboundConfigurationPackets1_21_6;\nimport com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ServerboundPacket1_21_6;\nimport com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ServerboundPackets1_21_6;\nimport com.viaversion.viaversion.rewriter.BlockRewriter;\nimport com.viaversion.viaversion.rewriter.ParticleRewriter;\nimport com.viaversion.viaversion.rewriter.RecipeDisplayRewriter;\nimport com.viaversion.viaversion.rewriter.TagRewriter;\nimport com.viaversion.viaversion.rewriter.block.BlockRewriter1_21_5;\nimport com.viaversion.viaversion.util.Key;\n\nimport static com.viaversion.viaversion.util.ProtocolUtil.packetTypeMap;\n\npublic final class Protocol1_21_6To1_21_5 extends BackwardsProtocol<ClientboundPacket1_21_6, ClientboundPacket1_21_5, ServerboundPacket1_21_6, ServerboundPacket1_21_5> {\n\n    public static final BackwardsMappingData MAPPINGS = new BackwardsMappingData(\"1.21.6\", \"1.21.5\", Protocol1_21_5To1_21_6.class);\n    private final EntityPacketRewriter1_21_6 entityRewriter = new EntityPacketRewriter1_21_6(this);\n    private final BlockItemPacketRewriter1_21_6 itemRewriter = new BlockItemPacketRewriter1_21_6(this);\n    private final ParticleRewriter<ClientboundPacket1_21_6> particleRewriter = new ParticleRewriter<>(this);\n    private final NBTComponentRewriter<ClientboundPacket1_21_6> translatableRewriter = new ComponentRewriter1_21_6(this);\n    private final TagRewriter<ClientboundPacket1_21_6> tagRewriter = new TagRewriter<>(this);\n    private final RecipeDisplayRewriter<ClientboundPacket1_21_6> recipeRewriter = new RecipeDisplayRewriter1_21_5<>(this);\n    private final BackwardsRegistryRewriter registryDataRewriter = new RegistryDataRewriter1_21_6(this);\n    private final BlockRewriter<ClientboundPacket1_21_6> blockRewriter = new BlockRewriter1_21_5<>(this, ChunkType1_21_5::new);\n\n    public Protocol1_21_6To1_21_5() {\n        super(ClientboundPacket1_21_6.class, ClientboundPacket1_21_5.class, ServerboundPacket1_21_6.class, ServerboundPacket1_21_5.class);\n    }\n\n    @Override\n    protected void registerPackets() {\n        super.registerPackets();\n\n        appendClientbound(ClientboundPackets1_21_6.SOUND, wrapper -> {\n            wrapper.passthrough(Types.VAR_INT); // Source\n            fixSoundSource(wrapper);\n        });\n        appendClientbound(ClientboundPackets1_21_6.SOUND_ENTITY, wrapper -> {\n            wrapper.passthrough(Types.VAR_INT); // Source\n            fixSoundSource(wrapper);\n        });\n        appendClientbound(ClientboundPackets1_21_6.STOP_SOUND, wrapper -> {\n            final byte flags = wrapper.get(Types.BYTE, 0);\n            if ((flags & 0x01) != 0) {\n                fixSoundSource(wrapper);\n            }\n        });\n\n        final CommandRewriter1_19_4<ClientboundPacket1_21_6> commandRewriter = new CommandRewriter1_19_4<>(this) {\n            @Override\n            public void handleArgument(final PacketWrapper wrapper, final String argumentType) {\n                if (argumentType.equals(\"minecraft:hex_color\") || argumentType.equals(\"minecraft:dialog\")) {\n                    wrapper.write(Types.VAR_INT, 0); // Word\n                } else {\n                    super.handleArgument(wrapper, argumentType);\n                }\n            }\n        };\n        replaceClientbound(ClientboundPackets1_21_6.COMMANDS, commandRewriter::handle1_19);\n\n        registerClientbound(ClientboundPackets1_21_6.CHANGE_DIFFICULTY, wrapper -> {\n            final int difficulty = wrapper.read(Types.VAR_INT);\n            wrapper.write(Types.UNSIGNED_BYTE, (short) difficulty);\n        });\n        registerServerbound(ServerboundPackets1_21_5.CHANGE_DIFFICULTY, wrapper -> {\n            final short difficulty = wrapper.read(Types.UNSIGNED_BYTE);\n            wrapper.write(Types.VAR_INT, (int) difficulty);\n        });\n        registerServerbound(ServerboundPackets1_21_5.CHAT_COMMAND, this::handleClickEvents);\n        registerServerbound(ServerboundPackets1_21_5.CHAT_COMMAND_SIGNED, this::handleClickEvents);\n\n        // Are you sure you want to see this? This is your last chance to turn back.\n        registerClientbound(ClientboundPackets1_21_6.SHOW_DIALOG, null, wrapper -> {\n            wrapper.cancel();\n            if (!ViaBackwards.getConfig().dialogsViaChests()) {\n                return;\n            }\n\n            final RegistryAndTags registryAndTags = wrapper.user().get(RegistryAndTags.class);\n            final ServerLinks serverLinks = wrapper.user().get(ServerLinks.class);\n\n            final Holder<CompoundTag> holder = wrapper.passthrough(Types.TRUSTED_COMPOUND_TAG_HOLDER);\n            final CompoundTag tag = holder.isDirect() ? holder.value() : registryAndTags.fromRegistry(holder.id());\n\n            final DialogViewProvider provider = Via.getManager().getProviders().get(DialogViewProvider.class);\n            provider.openDialog(wrapper.user(), new Dialog(registryAndTags, serverLinks, tag));\n        });\n        registerClientbound(ClientboundConfigurationPackets1_21_6.SHOW_DIALOG, null, wrapper -> {\n            wrapper.cancel();\n            if (!ViaBackwards.getConfig().dialogsViaChests()) {\n                return;\n            }\n\n            final RegistryAndTags registryAndTags = wrapper.user().get(RegistryAndTags.class);\n            final ServerLinks serverLinks = wrapper.user().get(ServerLinks.class);\n            final CompoundTag tag = wrapper.read(Types.TRUSTED_COMPOUND_TAG);\n\n            final DialogViewProvider provider = Via.getManager().getProviders().get(DialogViewProvider.class);\n            provider.openDialog(wrapper.user(), new Dialog(registryAndTags, serverLinks, tag));\n        });\n        registerClientbound(ClientboundPackets1_21_6.CLEAR_DIALOG, null, this::clearDialog);\n        registerClientbound(ClientboundConfigurationPackets1_21_6.CLEAR_DIALOG, null, this::clearDialog);\n\n        registerClientbound(ClientboundPackets1_21_6.SERVER_LINKS, this::storeServerLinks);\n        registerClientbound(ClientboundConfigurationPackets1_21_6.SERVER_LINKS, this::storeServerLinks);\n\n        // The ones below are specific to the chest dialog view provider\n        registerServerbound(ServerboundPackets1_21_5.CONTAINER_CLOSE, wrapper -> {\n            final ChestDialogStorage storage = wrapper.user().get(ChestDialogStorage.class);\n            if (storage == null) {\n                return;\n            }\n\n            final ChestDialogViewProvider provider = (ChestDialogViewProvider) Via.getManager().getProviders().get(DialogViewProvider.class);\n            if (storage.phase() == ChestDialogStorage.Phase.ANVIL_VIEW) {\n                wrapper.cancel();\n                provider.openChestView(wrapper.user(), storage, ChestDialogStorage.Phase.DIALOG_VIEW);\n                return;\n            }\n            if (storage.phase() == ChestDialogStorage.Phase.WAITING_FOR_RESPONSE) {\n                wrapper.cancel();\n                if (storage.closeButtonEnabled()) {\n                    provider.openChestView(wrapper.user(), storage, ChestDialogStorage.Phase.DIALOG_VIEW);\n                } else {\n                    provider.openChestView(wrapper.user(), storage, ChestDialogStorage.Phase.WAITING_FOR_RESPONSE);\n                }\n                return;\n            }\n\n            final boolean allowClosing = storage.allowClosing();\n            if (!allowClosing) {\n                wrapper.cancel();\n                if (storage.dialog().canCloseWithEscape()) {\n                    provider.clickButton(wrapper.user(), Dialog.AfterAction.CLOSE, storage.dialog().actionButton());\n                } else {\n                    provider.openChestView(wrapper.user(), storage, ChestDialogStorage.Phase.DIALOG_VIEW);\n                }\n            }\n            storage.setAllowClosing(false);\n        });\n        registerServerbound(ServerboundPackets1_21_5.RENAME_ITEM, wrapper -> {\n            final ChestDialogStorage storage = wrapper.user().get(ChestDialogStorage.class);\n            if (storage == null || storage.phase() != ChestDialogStorage.Phase.ANVIL_VIEW) {\n                return;\n            }\n\n            wrapper.cancel();\n            final String name = wrapper.read(Types.STRING);\n\n            final ChestDialogViewProvider provider = (ChestDialogViewProvider) Via.getManager().getProviders().get(DialogViewProvider.class);\n            provider.updateAnvilText(wrapper.user(), name);\n        });\n        appendServerbound(ServerboundPackets1_21_5.CONTAINER_CLICK, wrapper -> {\n            final ChestDialogViewProvider provider = (ChestDialogViewProvider) Via.getManager().getProviders().get(DialogViewProvider.class);\n            if (provider == null) {\n                return;\n            }\n\n            final int containerId = wrapper.get(Types.VAR_INT, 0);\n            final int slot = wrapper.get(Types.SHORT, 0);\n            final byte button = wrapper.get(Types.BYTE, 0);\n            final int mode = wrapper.get(Types.VAR_INT, 2);\n            if (provider.clickDialog(wrapper.user(), containerId, slot, button, mode)) {\n                wrapper.cancel();\n            }\n        });\n\n        cancelClientbound(ClientboundPackets1_21_6.TRACKED_WAYPOINT);\n    }\n\n    private void fixSoundSource(final PacketWrapper wrapper) {\n        final int source = wrapper.get(Types.VAR_INT, 0);\n        if (source == 10) { // New ui source, map to master\n            wrapper.set(Types.VAR_INT, 0, 0);\n        }\n    }\n\n    private void updateTags(final PacketWrapper wrapper) {\n        tagRewriter.handleGeneric(wrapper);\n        wrapper.resetReader();\n\n        final RegistryAndTags registryAndTags = wrapper.user().get(RegistryAndTags.class);\n        final int length = wrapper.passthrough(Types.VAR_INT);\n        for (int i = 0; i < length; i++) {\n            final String registryKey = wrapper.read(Types.STRING);\n            final boolean dialog = \"dialog\".equals(Key.stripMinecraftNamespace(registryKey));\n            if (dialog) {\n                final int tagsSize = wrapper.read(Types.VAR_INT);\n                for (int j = 0; j < tagsSize; j++) {\n                    final String key = wrapper.read(Types.STRING);\n                    final int[] ids = wrapper.read(Types.VAR_INT_ARRAY_PRIMITIVE);\n                    registryAndTags.storeTags(key, ids);\n                }\n            } else {\n                wrapper.write(Types.STRING, registryKey); // Write back\n                final int tagsSize = wrapper.passthrough(Types.VAR_INT);\n                for (int j = 0; j < tagsSize; j++) {\n                    wrapper.passthrough(Types.STRING);\n                    wrapper.passthrough(Types.VAR_INT_ARRAY_PRIMITIVE);\n                }\n            }\n        }\n\n        if (registryAndTags.tagsSent()) {\n            wrapper.set(Types.VAR_INT, 0, length - 1); // Dialog tags have been read, remove from size\n        }\n    }\n\n    private void clearDialog(final PacketWrapper wrapper) {\n        wrapper.cancel();\n        if (!ViaBackwards.getConfig().dialogsViaChests()) {\n            return;\n        }\n\n        final DialogViewProvider provider = Via.getManager().getProviders().get(DialogViewProvider.class);\n        provider.closeDialog(wrapper.user());\n    }\n\n    private void storeServerLinks(final PacketWrapper wrapper) {\n        final ServerLinks serverLinks = new ServerLinks();\n        final int length = wrapper.passthrough(Types.VAR_INT);\n        for (int i = 0; i < length; i++) {\n            if (wrapper.passthrough(Types.BOOLEAN)) {\n                final int id = wrapper.passthrough(Types.VAR_INT);\n                final String url = wrapper.passthrough(Types.STRING);\n                serverLinks.storeLink(id, url);\n            } else {\n                final Tag tag = wrapper.passthrough(Types.TRUSTED_TAG);\n                final String url = wrapper.passthrough(Types.STRING);\n                serverLinks.storeLink(tag, url);\n            }\n        }\n        wrapper.user().put(serverLinks);\n    }\n\n    private void handleClickEvents(final PacketWrapper wrapper) {\n        final String command = wrapper.passthrough(Types.STRING);\n\n        final ClickEvents clickEvents = wrapper.user().get(ClickEvents.class);\n        if (clickEvents.handleChatCommand(wrapper.user(), command)) {\n            wrapper.cancel();\n        }\n    }\n\n    @Override\n    public void init(final UserConnection user) {\n        addEntityTracker(user, new EntityTrackerBase(user, EntityTypes1_21_6.PLAYER));\n        addItemHasher(user, new ItemHasherBase(this, user));\n        user.put(new RegistryAndTags());\n        user.put(new ClickEvents());\n    }\n\n    @Override\n    public void register(final ViaProviders providers) {\n        providers.register(DialogViewProvider.class, new ChestDialogViewProvider(this));\n    }\n\n    @Override\n    public BackwardsMappingData getMappingData() {\n        return MAPPINGS;\n    }\n\n    @Override\n    public EntityPacketRewriter1_21_6 getEntityRewriter() {\n        return entityRewriter;\n    }\n\n    @Override\n    public BlockItemPacketRewriter1_21_6 getItemRewriter() {\n        return itemRewriter;\n    }\n\n    @Override\n    public BlockRewriter<ClientboundPacket1_21_6> getBlockRewriter() {\n        return blockRewriter;\n    }\n\n    @Override\n    public BackwardsRegistryRewriter getRegistryDataRewriter() {\n        return registryDataRewriter;\n    }\n\n    @Override\n    public ParticleRewriter<ClientboundPacket1_21_6> getParticleRewriter() {\n        return particleRewriter;\n    }\n\n    @Override\n    public NBTComponentRewriter<ClientboundPacket1_21_6> getComponentRewriter() {\n        return translatableRewriter;\n    }\n\n    @Override\n    public TagRewriter<ClientboundPacket1_21_6> getTagRewriter() {\n        return tagRewriter;\n    }\n\n    @Override\n    public RecipeDisplayRewriter<ClientboundPacket1_21_6> getRecipeRewriter() {\n        return recipeRewriter;\n    }\n\n    @Override\n    public VersionedTypesHolder types() {\n        return VersionedTypes.V1_21_6;\n    }\n\n    @Override\n    public VersionedTypesHolder mappedTypes() {\n        return VersionedTypes.V1_21_5;\n    }\n\n    @Override\n    protected PacketTypesProvider<ClientboundPacket1_21_6, ClientboundPacket1_21_5, ServerboundPacket1_21_6, ServerboundPacket1_21_5> createPacketTypesProvider() {\n        return new SimplePacketTypesProvider<>(\n            packetTypeMap(unmappedClientboundPacketType, ClientboundPackets1_21_6.class, ClientboundConfigurationPackets1_21_6.class),\n            packetTypeMap(mappedClientboundPacketType, ClientboundPackets1_21_5.class, ClientboundConfigurationPackets1_21.class),\n            packetTypeMap(mappedServerboundPacketType, ServerboundPackets1_21_6.class, ServerboundConfigurationPackets1_21_6.class),\n            packetTypeMap(unmappedServerboundPacketType, ServerboundPackets1_21_5.class, ServerboundConfigurationPackets1_20_5.class)\n        );\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_6to1_21_5/data/Button.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.data;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.Tag;\nimport com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.data.input.Input;\nimport com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.data.widget.Widget;\nimport com.viaversion.viaversion.util.Key;\nimport java.util.HashMap;\nimport java.util.Map;\nimport org.checkerframework.checker.nullness.qual.Nullable;\n\npublic class Button implements Widget {\n\n    public static final Button DEFAULT = defaulted();\n    private static final String[] CLICK_EVENTS = {\n        \"open_url\",\n        \"run_command\",\n        \"suggest_command\",\n        \"show_dialog\",\n        \"change_page\",\n        \"copy_to_clipboard\",\n        \"custom\"\n    };\n\n    private final Dialog dialog;\n    private final Tag label;\n    private final int width;\n\n    private final @Nullable Tag tooltip;\n    private @Nullable CompoundTag clickEvent;\n\n    private @Nullable Template template;\n\n    private @Nullable String id;\n    private @Nullable CompoundTag additions;\n\n    public Button(final Dialog dialog, final CompoundTag tag) {\n        this.dialog = dialog;\n        this.label = tag.get(\"label\");\n        this.tooltip = tag.get(\"tooltip\");\n\n        final int width = tag.getInt(\"width\", 150);\n        if (width < 1 || width > 1024) {\n            throw new IllegalArgumentException(\"Width must be between 1 and 1024, got: \" + width);\n        }\n        this.width = width;\n\n        final CompoundTag actionTag = tag.getCompoundTag(\"action\");\n        if (actionTag == null) {\n            return;\n        }\n\n        final String type = actionTag.getString(\"type\");\n        if (type == null) {\n            throw new IllegalArgumentException(\"Action type is missing in tag: \" + tag);\n        }\n\n        for (final String event : CLICK_EVENTS) {\n            if (Key.stripMinecraftNamespace(type).equals(event)) {\n                clickEvent = actionTag.copy();\n                clickEvent.put(\"action\", clickEvent.remove(\"type\"));\n                return;\n            }\n        }\n\n        if (Key.stripMinecraftNamespace(type).equals(\"dynamic/run_command\")) {\n            template = Template.fromString(actionTag.getString(\"template\"));\n        } else if (Key.stripMinecraftNamespace(type).equals(\"dynamic/custom\")) {\n            id = actionTag.getString(\"id\");\n            additions = actionTag.getCompoundTag(\"additions\");\n        }\n    }\n\n    private static Button defaulted() {\n        final CompoundTag tag = new CompoundTag();\n        final CompoundTag label = new CompoundTag();\n        tag.put(\"label\", label);\n        label.putString(\"translate\", \"gui.ok\");\n\n        return new Button(null, tag);\n    }\n\n    public static Button openUrl(final Tag label, final String url) {\n        final CompoundTag tag = new CompoundTag();\n        final CompoundTag actionTag = new CompoundTag();\n        final CompoundTag openUrlTag = new CompoundTag();\n\n        tag.put(\"label\", label);\n        tag.put(\"action\", actionTag);\n        actionTag.putString(\"type\", \"open_url\");\n        actionTag.put(\"action\", openUrlTag);\n        openUrlTag.putString(\"url\", url);\n        return new Button(null, tag);\n    }\n\n    /**\n     * Creates a click event for this button based on the provided inputs.\n     *\n     * @return a CompoundTag representing the click event, which can be used in a button action.\n     */\n    public @Nullable CompoundTag clickEvent() {\n        if (clickEvent != null) {\n            return clickEvent;\n        }\n\n        if (dialog == null) {\n            return null;\n        }\n\n        final Input[] inputs = dialog.widgets()\n            .stream()\n            .filter(widget -> widget instanceof Input)\n            .toArray(Input[]::new);\n\n        if (template != null) {\n            final Map<String, String> substitutions = new HashMap<>();\n            for (final Input input : inputs) {\n                substitutions.put(input.key(), input.asCommandSubstitution());\n            }\n\n            final CompoundTag tag = new CompoundTag();\n            tag.putString(\"action\", \"run_command\");\n            tag.putString(\"command\", template.instantiate(substitutions));\n            return tag;\n        }\n\n        if (id != null) {\n            final CompoundTag additions = this.additions != null ? this.additions.copy() : new CompoundTag();\n            for (final Input input : inputs) {\n                additions.put(input.key(), input.asTag());\n            }\n\n            final CompoundTag tag = new CompoundTag();\n            tag.putString(\"action\", \"custom\");\n            tag.putString(\"id\", id);\n            tag.put(\"payload\", additions);\n            return tag;\n        }\n\n        return null;\n    }\n\n    public Tag label() {\n        return label;\n    }\n\n    public @Nullable Tag tooltip() {\n        return tooltip;\n    }\n\n    public int width() {\n        return width;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_6to1_21_5/data/Dialog.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.data;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.ListTag;\nimport com.viaversion.nbt.tag.StringTag;\nimport com.viaversion.nbt.tag.Tag;\nimport com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.data.input.BooleanInput;\nimport com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.data.input.NumberRangeInput;\nimport com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.data.input.SingleOptionInput;\nimport com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.data.input.TextInput;\nimport com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.data.widget.ItemWidget;\nimport com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.data.widget.TextWidget;\nimport com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.data.widget.Widget;\nimport com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.provider.ChestDialogViewProvider;\nimport com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.storage.RegistryAndTags;\nimport com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.storage.ServerLinks;\nimport com.viaversion.viaversion.util.Key;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Map;\nimport org.checkerframework.checker.nullness.qual.Nullable;\n\n/**\n * The dialog structure. See the used subclasses for further details on the\n * specific structure. Note that the data hold here is directly from the server\n * and needs to be remapped manually when used. See {@link ChestDialogViewProvider} for an example\n */\npublic final class Dialog implements Widget {\n\n    private final List<Widget> widgets = new ArrayList<>();\n    private final Tag title;\n    private final @Nullable Tag externalTitle;\n    private final boolean canCloseWithEscape;\n    private final AfterAction afterAction;\n\n    private @Nullable Button actionButton;\n    private @Nullable Button yesButton;\n    private @Nullable Button noButton;\n\n    private int columns;\n    private int buttonWidth;\n\n    public Dialog(final RegistryAndTags registryAndTags, final ServerLinks serverLinks, final CompoundTag tag) {\n        final String type = tag.getString(\"type\");\n        if (type == null) {\n            throw new IllegalArgumentException(\"Dialog type is missing in tag: \" + tag);\n        }\n\n        this.title = tag.get(\"title\");\n        this.externalTitle = tag.get(\"external_title\");\n        this.canCloseWithEscape = tag.getBoolean(\"can_close_with_escape\", true);\n        this.afterAction = AfterAction.valueOf(tag.getString(\"after_action\", \"close\").toUpperCase(Locale.ROOT));\n\n        ListTag<CompoundTag> bodyListTag = tag.getListTag(\"body\", CompoundTag.class);\n        if (bodyListTag == null) {\n            CompoundTag bodyTag = tag.getCompoundTag(\"body\");\n            if (bodyTag != null) {\n                bodyListTag = new ListTag<>(CompoundTag.class);\n                bodyListTag.add(bodyTag);\n            }\n        }\n        if (bodyListTag != null) {\n            for (final CompoundTag bodyTag : bodyListTag) {\n                fillBodyWidget(bodyTag);\n            }\n        }\n\n        final ListTag<CompoundTag> inputListTag = tag.getListTag(\"inputs\", CompoundTag.class);\n        if (inputListTag != null) {\n            for (final CompoundTag inputTag : inputListTag) {\n                fillInputWidget(inputTag);\n            }\n        }\n\n        switch (Key.stripMinecraftNamespace(type)) {\n            case \"notice\" -> fillNoticeDialog(tag);\n            case \"server_links\" -> fillServerLinksDialog(serverLinks, tag);\n            case \"dialog_list\" -> fillDialogList(registryAndTags, serverLinks, tag);\n            case \"multi_action\" -> fillMultiActionDialog(tag);\n            case \"confirmation\" -> fillConfirmationDialog(tag);\n            default -> throw new IllegalArgumentException(\"Unknown dialog type: \" + type + \" in tag: \" + tag);\n        }\n    }\n\n    private void fillBodyWidget(final CompoundTag tag) {\n        final String type = tag.getString(\"type\");\n        if (type == null) {\n            throw new IllegalArgumentException(\"Dialog type is missing in tag: \" + tag);\n        }\n\n        if (Key.stripMinecraftNamespace(type).equals(\"plain_message\")) {\n            widgets.add(new TextWidget(tag));\n        } else if (Key.stripMinecraftNamespace(type).equals(\"item\")) {\n            widgets.add(new ItemWidget(tag));\n        } else {\n            throw new IllegalArgumentException(\"Unknown dialog body type: \" + type + \" in tag: \" + tag);\n        }\n    }\n\n    private void fillInputWidget(final CompoundTag tag) {\n        final String type = tag.getString(\"type\");\n        if (type == null) {\n            throw new IllegalArgumentException(\"Dialog type is missing in tag: \" + tag);\n        }\n\n        widgets.add(switch (Key.stripMinecraftNamespace(type)) {\n            case \"boolean\" -> new BooleanInput(tag);\n            case \"number_range\" -> new NumberRangeInput(tag);\n            case \"single_option\" -> new SingleOptionInput(tag);\n            case \"text\" -> new TextInput(tag);\n            default -> throw new IllegalArgumentException(\"Unknown dialog input type: \" + type + \" in tag: \" + tag);\n        });\n    }\n\n    private void fillNoticeDialog(final CompoundTag tag) {\n        final CompoundTag actionTag = tag.getCompoundTag(\"action\");\n        actionButton = actionTag == null ? Button.DEFAULT : new Button(this, actionTag);\n    }\n\n    private void fillServerLinksDialog(final ServerLinks serverLinks, final CompoundTag tag) {\n        fillDialogBase(tag);\n        buttonWidth = tag.getInt(\"button_width\", 150);\n\n        if (serverLinks == null) {\n            return;\n        }\n        for (final Map.Entry<Tag, String> entry : serverLinks.links().entrySet()) {\n            final Tag label = entry.getKey();\n            final String url = entry.getValue();\n            widgets.add(Button.openUrl(label, url));\n        }\n    }\n\n    private void fillDialogList(final RegistryAndTags registryAndTags, final ServerLinks serverLinks, final CompoundTag tag) {\n        // Hold them as either a list of inlined / singleton inlined, a list of registry entries / singleton entry or a tag.\n        ListTag<CompoundTag> dialogsTag = tag.getListTag(\"dialogs\", CompoundTag.class);\n        if (dialogsTag == null) {\n            CompoundTag dialogTag = tag.getCompoundTag(\"dialogs\");\n            if (dialogTag != null) {\n                dialogsTag = new ListTag<>(CompoundTag.class);\n                dialogsTag.add(dialogTag);\n            }\n\n            ListTag<StringTag> registryDialogsTag = tag.getListTag(\"dialogs\", StringTag.class);\n            StringTag registryDialogTag = tag.getStringTag(\"dialogs\");\n            if (registryDialogTag != null) {\n                registryDialogsTag = new ListTag<>(StringTag.class);\n                registryDialogsTag.add(registryDialogTag);\n            }\n            if (registryDialogsTag != null) {\n                dialogsTag = new ListTag<>(CompoundTag.class);\n                for (final StringTag nameTag : registryDialogsTag) {\n                    final String key = nameTag.getValue();\n                    if (key.startsWith(\"#\")) {\n                        for (final CompoundTag entry : registryAndTags.fromRegistryKey(key.substring(1))) {\n                            dialogsTag.add(entry);\n                        }\n                    } else {\n                        dialogsTag.add(registryAndTags.fromRegistry(key));\n                    }\n                }\n            }\n        }\n        widgets.addAll(dialogsTag.stream().map(dialog -> new Dialog(registryAndTags, serverLinks, dialog)).toList());\n\n        fillDialogBase(tag);\n        buttonWidth = tag.getInt(\"button_width\", 150);\n    }\n\n    private void fillDialogBase(final CompoundTag tag) {\n        final CompoundTag exitActionTag = tag.getCompoundTag(\"exit_action\");\n        actionButton = exitActionTag == null ? null : new Button(this, exitActionTag);\n\n        final int columns = tag.getInt(\"columns\", 2);\n        if (columns < 1) {\n            throw new IllegalArgumentException(\"Columns must be non-negative, got: \" + columns);\n        }\n        this.columns = columns;\n    }\n\n    private void fillMultiActionDialog(final CompoundTag tag) {\n        final ListTag<CompoundTag> actionsTag = tag.getListTag(\"actions\", CompoundTag.class);\n        if (actionsTag == null || actionsTag.isEmpty()) {\n            throw new IllegalArgumentException(\"Actions must not be empty in tag: \" + tag);\n        }\n        widgets.addAll(actionsTag.stream().map(actionTag -> new Button(this, actionTag)).toList());\n\n        fillDialogBase(tag);\n    }\n\n    private void fillConfirmationDialog(final CompoundTag tag) {\n        yesButton = new Button(this, tag.getCompoundTag(\"yes\"));\n        noButton = new Button(this, tag.getCompoundTag(\"no\"));\n    }\n\n    public Tag title() {\n        return title;\n    }\n\n    public @Nullable Tag externalTitle() {\n        return externalTitle;\n    }\n\n    public boolean canCloseWithEscape() {\n        return canCloseWithEscape;\n    }\n\n    public AfterAction afterAction() {\n        return afterAction;\n    }\n\n    public enum AfterAction {\n        CLOSE,\n        NONE,\n        WAIT_FOR_RESPONSE\n    }\n\n    public @Nullable Button actionButton() {\n        return actionButton;\n    }\n\n    public @Nullable Button yesButton() {\n        return yesButton;\n    }\n\n    public @Nullable Button noButton() {\n        return noButton;\n    }\n\n    public int columns() {\n        return columns;\n    }\n\n    public int buttonWidth() {\n        return buttonWidth;\n    }\n\n    public List<Widget> widgets() {\n        return widgets;\n    }\n\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_6to1_21_5/data/Template.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.data;\n\nimport com.google.common.collect.ImmutableList;\nimport com.google.common.collect.ImmutableList.Builder;\nimport java.util.List;\nimport java.util.Map;\n\npublic record Template(List<String> segments, List<String> variables) {\n\n    private static final int MAX_LENGTH = 2000000;\n\n    public static Template fromString(final String string) {\n        final Builder<String> segmentsBuilder = ImmutableList.builder();\n        final Builder<String> variablesBuilder = ImmutableList.builder();\n        int currentIndex = 0;\n        int dollarIndex = string.indexOf('$');\n\n        while (dollarIndex != -1) {\n            if (dollarIndex != string.length() - 1 && string.charAt(dollarIndex + 1) == '(') {\n                segmentsBuilder.add(string.substring(currentIndex, dollarIndex));\n\n                final int closingParenIndex = string.indexOf(41, dollarIndex + 1);\n                if (closingParenIndex == -1) {\n                    throw new IllegalArgumentException(\"Unterminated macro variable\");\n                }\n\n                final String variableName = string.substring(dollarIndex + 2, closingParenIndex);\n                if (!isValidVariableName(variableName)) {\n                    throw new IllegalArgumentException(\"Invalid macro variable name '\" + variableName + \"'\");\n                }\n\n                variablesBuilder.add(variableName);\n                currentIndex = closingParenIndex + 1;\n                dollarIndex = string.indexOf(36, currentIndex);\n            } else {\n                dollarIndex = string.indexOf(36, dollarIndex + 1);\n            }\n        }\n\n        if (currentIndex == 0) {\n            throw new IllegalArgumentException(\"No variables in macro\");\n        }\n        if (currentIndex != string.length()) {\n            segmentsBuilder.add(string.substring(currentIndex));\n        }\n        return new Template(segmentsBuilder.build(), variablesBuilder.build());\n    }\n\n    private static boolean isValidVariableName(final String string) {\n        for (int i = 0; i < string.length(); i++) {\n            final char c = string.charAt(i);\n            if (!Character.isLetterOrDigit(c) && c != '_') {\n                return false;\n            }\n        }\n\n        return true;\n    }\n\n    public String instantiate(final Map<String, String> map) {\n        return substitute(variables().stream().map(string -> map.getOrDefault(string, \"\")).toList());\n    }\n\n    private String substitute(final List<String> list) {\n        final StringBuilder out = new StringBuilder();\n        for (int i = 0; i < this.variables.size(); i++) {\n            out.append(this.segments.get(i)).append(list.get(i));\n            if (out.length() > MAX_LENGTH) {\n                throw new IllegalArgumentException(\"Output too long (> \" + MAX_LENGTH + \")\");\n            }\n        }\n        if (this.segments.size() > this.variables.size()) {\n            out.append(this.segments.get(this.segments.size() - 1));\n            if (out.length() > MAX_LENGTH) {\n                throw new IllegalArgumentException(\"Output too long (> \" + MAX_LENGTH + \")\");\n            }\n        }\n        return out.toString();\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_6to1_21_5/data/input/BooleanInput.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.data.input;\n\nimport com.viaversion.nbt.tag.ByteTag;\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.Tag;\n\npublic final class BooleanInput implements Input {\n\n    private final String key;\n    private final Tag label;\n    private final boolean initial;\n    private final String onTrue;\n    private final String onFalse;\n\n    private boolean value;\n\n    public BooleanInput(final CompoundTag tag) {\n        this.key = tag.getString(\"key\");\n        this.label = tag.get(\"label\");\n        this.initial = tag.getBoolean(\"initial\", false);\n        this.onTrue = tag.getString(\"on_true\", \"true\");\n        this.onFalse = tag.getString(\"on_false\", \"false\");\n\n        this.value = initial;\n    }\n\n    @Override\n    public String key() {\n        return key;\n    }\n\n    @Override\n    public String asCommandSubstitution() {\n        return value ? onTrue : onFalse;\n    }\n\n    @Override\n    public Tag asTag() {\n        return new ByteTag(value);\n    }\n\n    public Tag label() {\n        return label;\n    }\n\n    public boolean initial() {\n        return initial;\n    }\n\n    public String onTrue() {\n        return onTrue;\n    }\n\n    public String onFalse() {\n        return onFalse;\n    }\n\n    public boolean value() {\n        return value;\n    }\n\n    public void setValue(final boolean value) {\n        this.value = value;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_6to1_21_5/data/input/Input.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.data.input;\n\nimport com.viaversion.nbt.tag.Tag;\nimport com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.data.widget.Widget;\n\npublic interface Input extends Widget {\n\n    String key();\n\n    String asCommandSubstitution();\n\n    Tag asTag();\n\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_6to1_21_5/data/input/NumberRangeInput.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.data.input;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.FloatTag;\nimport com.viaversion.nbt.tag.Tag;\n\nimport static com.viaversion.viabackwards.utils.ChatUtil.translate;\n\npublic final class NumberRangeInput implements Input {\n\n    private final String key;\n    private final Tag label;\n    private final String labelFormat;\n    private final float start;\n    private final float end;\n    private final Float initial;\n    private final Float step;\n\n    private float value;\n\n    public NumberRangeInput(final CompoundTag tag) {\n        this.key = tag.getString(\"key\");\n        this.label = tag.get(\"label\");\n        this.labelFormat = tag.getString(\"label_format\", \"options.generic_value\");\n        this.start = tag.getFloat(\"start\");\n        this.end = tag.getFloat(\"end\");\n        this.initial = tag.getFloat(\"initial\", (this.start + this.end) / 2F);\n        final FloatTag stepTag = tag.getFloatTag(\"step\");\n        if (stepTag != null && stepTag.asFloat() < 0F) {\n            throw new IllegalArgumentException(\"Step must be non-negative, got: \" + stepTag.asFloat());\n        }\n        this.step = stepTag == null ? -1F : stepTag.asFloat();\n\n        this.value = initial;\n    }\n\n    @Override\n    public String key() {\n        return key;\n    }\n\n    @Override\n    public String asCommandSubstitution() {\n        return valueAsString();\n    }\n\n    @Override\n    public Tag asTag() {\n        return new FloatTag(value);\n    }\n\n    public Tag label() {\n        return label;\n    }\n\n    public String labelFormat() {\n        return labelFormat;\n    }\n\n    public float start() {\n        return start;\n    }\n\n    public float end() {\n        return end;\n    }\n\n    public float initial() {\n        return initial;\n    }\n\n    public float step() {\n        return step;\n    }\n\n    public float value() {\n        return value;\n    }\n\n    public String valueAsString() {\n        final int asInt = (int) value;\n        return asInt == value ? Integer.toString(asInt) : Float.toString(value);\n    }\n\n    public void setValue(final float value) {\n        this.value = value;\n    }\n\n    public void setClampedValue(final float value) {\n        this.value = (value < start) ? start : Math.min(value, end);\n    }\n\n    public Tag displayName() {\n        return translate(labelFormat, label, translate(valueAsString()));\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_6to1_21_5/data/input/SingleOptionInput.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.data.input;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.ListTag;\nimport com.viaversion.nbt.tag.StringTag;\nimport com.viaversion.nbt.tag.Tag;\nimport java.util.Objects;\nimport org.checkerframework.checker.nullness.qual.Nullable;\n\npublic final class SingleOptionInput implements Input {\n\n    private final String key;\n    private final Entry[] options;\n    private final @Nullable Tag label;\n\n    private int value;\n\n    public SingleOptionInput(final CompoundTag tag) {\n        final ListTag<CompoundTag> options = tag.getListTag(\"options\", CompoundTag.class);\n        if (options == null || options.isEmpty()) {\n            throw new IllegalArgumentException(\"Options must not be empty in tag: \" + tag);\n        }\n\n        this.key = tag.getString(\"key\");\n        this.options = options.stream().map(Entry::new).toArray(Entry[]::new);\n        this.label = tag.getBoolean(\"label_visible\", true) ? tag.get(\"label\") : null;\n\n        for (int i = 0; i < this.options.length; i++) {\n            if (this.options[i].initial()) {\n                if (this.value != 0) {\n                    throw new IllegalArgumentException(\"Multiple initial options found in tag: \" + tag);\n                }\n                this.value = i;\n            }\n        }\n    }\n\n    @Override\n    public String key() {\n        return key;\n    }\n\n    @Override\n    public String asCommandSubstitution() {\n        return this.options[value].id;\n    }\n\n    @Override\n    public Tag asTag() {\n        return new StringTag(asCommandSubstitution());\n    }\n\n    public Entry[] options() {\n        return options;\n    }\n\n    public @Nullable Tag label() {\n        return label;\n    }\n\n    public int value() {\n        return value;\n    }\n\n    public void setValue(final int value) {\n        if (value < 0 || value >= options.length) {\n            throw new IllegalArgumentException(\"Value must be between 0 and \" + (options.length - 1));\n        }\n        this.value = value;\n    }\n\n    public void setClampedValue(final int value) {\n        if (value < 0) {\n            this.value = 0;\n        } else if (value >= options.length) {\n            this.value = 0;\n        } else {\n            this.value = value;\n        }\n    }\n\n    public static class Entry {\n        private final String id;\n        private final Tag display;\n        private final boolean initial;\n\n        public Entry(final CompoundTag tag) {\n            id = tag.getString(\"id\");\n            display = tag.get(\"display\");\n            initial = tag.getBoolean(\"initial\", false);\n        }\n\n        public String id() {\n            return id;\n        }\n\n        public Tag display() {\n            return display;\n        }\n\n        public boolean initial() {\n            return initial;\n        }\n\n        public Tag computeDisplay() {\n            return Objects.requireNonNullElseGet(display, () -> new StringTag(id));\n        }\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_6to1_21_5/data/input/TextInput.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.data.input;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.IntTag;\nimport com.viaversion.nbt.tag.ListTag;\nimport com.viaversion.nbt.tag.StringTag;\nimport com.viaversion.nbt.tag.Tag;\nimport org.checkerframework.checker.nullness.qual.Nullable;\n\npublic final class TextInput implements Input {\n\n    private final String key;\n    private final @Nullable Tag label;\n    private final String initial;\n    private final int maxLength;\n    private final @Nullable MultilineOptions[] options;\n\n    private String value;\n\n    public TextInput(final CompoundTag tag) {\n        this.key = tag.getString(\"key\");\n        this.label = tag.getBoolean(\"label_visible\", true) ? tag.get(\"label\") : null;\n        this.initial = tag.getString(\"initial\", \"\");\n        this.maxLength = tag.getInt(\"max_length\", 32);\n        if (this.maxLength < 1) {\n            throw new IllegalArgumentException(\"Max length must be at least 1, got: \" + this.maxLength);\n        }\n        final ListTag<CompoundTag> multilineList = tag.getListTag(\"multiline\", CompoundTag.class);\n        if (multilineList != null && !multilineList.isEmpty()) {\n            this.options = multilineList.stream().map(MultilineOptions::new).toArray(MultilineOptions[]::new);\n        } else {\n            this.options = null;\n        }\n\n        this.value = initial;\n    }\n\n    @Override\n    public String key() {\n        return key;\n    }\n\n    @Override\n    public String asCommandSubstitution() {\n        return value;\n    }\n\n    @Override\n    public Tag asTag() {\n        return new StringTag(value);\n    }\n\n    public @Nullable Tag label() {\n        return label;\n    }\n\n    public String initial() {\n        return initial;\n    }\n\n    public int maxLength() {\n        return maxLength;\n    }\n\n    public @Nullable MultilineOptions[] options() {\n        return options;\n    }\n\n    public String value() {\n        return value;\n    }\n\n    public void setValue(final String value) {\n        if (value.length() > maxLength) {\n            throw new IllegalArgumentException(\"Value exceeds max length of \" + maxLength + \": \" + value);\n        }\n        this.value = value;\n    }\n\n    public void setClampedValue(final String value) {\n        if (value.length() > maxLength) {\n            this.value = value.substring(0, maxLength);\n        } else {\n            this.value = value;\n        }\n    }\n\n    public static class MultilineOptions {\n\n        private final @Nullable Integer maxLines;\n\n        public MultilineOptions(final CompoundTag tag) {\n            final IntTag maxLinesTag = tag.getIntTag(\"max_lines\");\n            if (maxLinesTag != null && maxLinesTag.asInt() < 1) {\n                throw new IllegalArgumentException(\"Max lines must be at least 1, got: \" + maxLinesTag);\n            }\n            this.maxLines = maxLinesTag != null ? maxLinesTag.asInt() : null;\n        }\n\n        public @Nullable Integer maxLines() {\n            return maxLines;\n        }\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_6to1_21_5/data/widget/ItemWidget.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.data.widget;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport org.checkerframework.checker.nullness.qual.Nullable;\n\npublic final class ItemWidget implements Widget {\n\n    private final CompoundTag item;\n    private final @Nullable TextWidget description;\n    private final boolean showTooltip;\n    private final int width;\n    private final int height;\n\n    public ItemWidget(final CompoundTag tag) {\n        this.item = tag.getCompoundTag(\"item\");\n        if (this.item == null) {\n            throw new IllegalArgumentException(\"Item tag is missing in ItemWidget tag: \" + tag);\n        }\n        this.description = tag.contains(\"description\") ? new TextWidget(tag.getCompoundTag(\"description\")) : null;\n        this.showTooltip = tag.getBoolean(\"show_tooltip\", true);\n        final int width = tag.getInt(\"width\", 16);\n        final int height = tag.getInt(\"height\", 16);\n        if (width < 1 || width > 256) {\n            throw new IllegalArgumentException(\"Width must be between 1 and 256, got: \" + width);\n        }\n        if (height < 1 || height > 256) {\n            throw new IllegalArgumentException(\"Height must be between 1 and 256, got: \" + height);\n        }\n        this.width = width;\n        this.height = height;\n    }\n\n    public CompoundTag item() {\n        return item;\n    }\n\n    public @Nullable TextWidget description() {\n        return description;\n    }\n\n    public boolean showTooltip() {\n        return showTooltip;\n    }\n\n    public int width() {\n        return width;\n    }\n\n    public int height() {\n        return height;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_6to1_21_5/data/widget/TextWidget.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.data.widget;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.Tag;\n\npublic final class TextWidget implements Widget {\n\n    private final Tag label;\n    private final int width;\n\n    public TextWidget(final CompoundTag tag) {\n        this.label = tag.get(\"contents\");\n        this.width = tag.getInt(\"width\", 200);\n    }\n\n    public Tag label() {\n        return label;\n    }\n\n    public int width() {\n        return width;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_6to1_21_5/data/widget/Widget.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.data.widget;\n\npublic interface Widget {\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_6to1_21_5/provider/ChestDialogViewProvider.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.provider;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.Tag;\nimport com.viaversion.viabackwards.ViaBackwards;\nimport com.viaversion.viabackwards.api.DialogStyleConfig;\nimport com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.Protocol1_21_6To1_21_5;\nimport com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.data.Button;\nimport com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.data.Dialog;\nimport com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.data.input.BooleanInput;\nimport com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.data.input.NumberRangeInput;\nimport com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.data.input.SingleOptionInput;\nimport com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.data.input.TextInput;\nimport com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.data.widget.ItemWidget;\nimport com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.data.widget.TextWidget;\nimport com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.data.widget.Widget;\nimport com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.storage.ChestDialogStorage;\nimport com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.storage.ClickEvents;\nimport com.viaversion.viabackwards.utils.ChatUtil;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.data.StructuredDataContainer;\nimport com.viaversion.viaversion.api.minecraft.data.StructuredDataKey;\nimport com.viaversion.viaversion.api.minecraft.item.Item;\nimport com.viaversion.viaversion.api.minecraft.item.StructuredItem;\nimport com.viaversion.viaversion.api.minecraft.item.data.TooltipDisplay;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.protocol.packet.State;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.version.VersionedTypes;\nimport com.viaversion.viaversion.libs.fastutil.ints.IntSortedSets;\nimport com.viaversion.viaversion.protocols.v1_21_4to1_21_5.packet.ClientboundPackets1_21_5;\nimport com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ServerboundPackets1_21_6;\nimport com.viaversion.viaversion.util.Key;\nimport com.viaversion.viaversion.util.MathUtil;\nimport java.util.ArrayList;\nimport java.util.List;\nimport org.checkerframework.checker.nullness.qual.Nullable;\n\nimport static com.viaversion.viabackwards.utils.ChatUtil.fixStyle;\nimport static com.viaversion.viabackwards.utils.ChatUtil.translate;\n\n/**\n * Default dialog view emulator. The layout has been stretched into a lot sub-functions for plugins to override.\n */\npublic class ChestDialogViewProvider implements DialogViewProvider {\n\n    private static final int INVENTORY_SIZE = 27;\n    private static final int INVENTORY_LAST_ROW = INVENTORY_SIZE - 9;\n\n    private final Protocol1_21_6To1_21_5 protocol;\n\n    public ChestDialogViewProvider(final Protocol1_21_6To1_21_5 protocol) {\n        this.protocol = protocol;\n    }\n\n    @Override\n    public void openDialog(final UserConnection connection, final Dialog dialog) {\n        final State state = connection.getProtocolInfo().getClientState();\n        if (state == State.CONFIGURATION) {\n            // TODO Implement by ending and re-starting the configuration phase\n            return;\n        }\n\n        // Batch text widgets following one another into a single MultiTextWidget to be display properly.\n        final List<Tag> texts = new ArrayList<>();\n        for (final Widget widget : new ArrayList<>(dialog.widgets())) {\n            if (widget instanceof final TextWidget textWidget) {\n                texts.add(textWidget.label());\n                dialog.widgets().remove(textWidget);\n            } else if (!texts.isEmpty()) {\n                // Flush collected texts before adding the non-text widget\n                dialog.widgets().add(dialog.widgets().indexOf(widget), new MultiTextWidget(texts.toArray(Tag[]::new)));\n                texts.clear();\n            }\n        }\n\n        // Flush remaining texts if any\n        if (!texts.isEmpty()) {\n            dialog.widgets().add(new MultiTextWidget(texts.toArray(Tag[]::new)));\n            texts.clear();\n        }\n\n        final ChestDialogStorage previousStorage = connection.get(ChestDialogStorage.class);\n        final ChestDialogStorage storage = new ChestDialogStorage(this, dialog);\n        if (previousStorage != null) {\n            storage.setPreviousDialog(previousStorage.dialog());\n        }\n        connection.put(storage);\n\n        openChestView(connection, storage, ChestDialogStorage.Phase.DIALOG_VIEW);\n    }\n\n    public void openChestView(final UserConnection connection, final ChestDialogStorage storage, ChestDialogStorage.Phase phase) {\n        storage.setPhase(connection, phase);\n\n        final PacketWrapper openScreen = PacketWrapper.create(ClientboundPackets1_21_5.OPEN_SCREEN, connection);\n        openScreen.write(Types.VAR_INT, storage.containerId());\n        openScreen.write(Types.VAR_INT, 2); // Container type id\n        openScreen.write(Types.TRUSTED_TAG, handleTag(connection, storage.dialog().title()));\n        openScreen.send(Protocol1_21_6To1_21_5.class);\n        updateDialog(connection, storage.dialog());\n    }\n\n    @Override\n    public void closeDialog(final UserConnection connection) {\n        final State state = connection.getProtocolInfo().getClientState();\n        if (state == State.CONFIGURATION) {\n            // TODO Implement by ending and re-starting the configuration phase\n            return;\n        }\n\n        final ChestDialogStorage storage = connection.get(ChestDialogStorage.class);\n        if (storage == null) {\n            return;\n        }\n\n        final PacketWrapper containerClose = PacketWrapper.create(ClientboundPackets1_21_5.CONTAINER_CLOSE, connection);\n        containerClose.write(Types.VAR_INT, storage.containerId());\n        containerClose.send(Protocol1_21_6To1_21_5.class);\n        if (storage.previousDialog() != null) {\n            openDialog(connection, storage.previousDialog());\n        } else {\n            connection.remove(ChestDialogStorage.class);\n        }\n    }\n\n    public boolean clickDialog(final UserConnection connection, final int container, int slot, final byte mouse, final int mode) {\n        final ChestDialogStorage storage = connection.get(ChestDialogStorage.class);\n        if (storage == null || storage.containerId() != container) {\n            return false;\n        }\n\n        if (mode != 0 || slot < 0 || slot >= INVENTORY_SIZE) {\n            updateDialog(connection, storage.dialog()); // Resync inventory view\n            return true;\n        }\n\n        if (storage.phase() == ChestDialogStorage.Phase.ANVIL_VIEW) {\n            openChestView(connection, storage, ChestDialogStorage.Phase.DIALOG_VIEW);\n            return true;\n        }\n\n        if (storage.phase() == ChestDialogStorage.Phase.WAITING_FOR_RESPONSE) {\n            if (slot == storage.actionIndex() && storage.closeButtonEnabled()) {\n                closeDialog(connection);\n            } else {\n                updateDialog(connection, storage.dialog()); // Resync inventory view\n            }\n            return true;\n        }\n\n        // Page navigation\n        if (slot == INVENTORY_SIZE - 1) {\n            final int pages = MathUtil.ceil(storage.items().length / (float) INVENTORY_LAST_ROW);\n            if (mouse == 0) {\n                storage.page++;\n            } else if (mouse == 1) {\n                storage.page--;\n            }\n            storage.page = MathUtil.clamp(storage.page, 0, pages - 1);\n        }\n        slot += storage.page * INVENTORY_LAST_ROW;\n\n        // Input widgets\n        final List<Widget> widgets = storage.dialog().widgets();\n        if (slot < widgets.size()) {\n            final Widget widget = widgets.get(slot);\n            if (widget instanceof final BooleanInput booleanInput) {\n                clickBooleanInput(booleanInput);\n            } else if (widget instanceof final NumberRangeInput numberRangeInput) {\n                clickNumberRangeInput(numberRangeInput, mouse);\n            } else if (widget instanceof final TextInput textInput) {\n                clickTextInput(connection, textInput);\n            } else if (widget instanceof final SingleOptionInput singleOptionInput) {\n                clickSingleOptionInput(singleOptionInput);\n            } else if (widget instanceof final Button button) {\n                clickButton(connection, storage.dialog().afterAction(), button);\n            } else if (widget instanceof final Dialog dialog) {\n                clickDialogButton(connection, dialog);\n            }\n        }\n\n        // And some special cases.\n        if (slot == storage.confirmationYesIndex()) {\n            final Button yesButton = storage.dialog().yesButton();\n            if (yesButton != null) {\n                clickButton(connection, storage.dialog().afterAction(), yesButton);\n            }\n        }\n        if (slot == storage.confirmationNoIndex()) {\n            final Button noButton = storage.dialog().noButton();\n            if (noButton != null) {\n                clickButton(connection, storage.dialog().afterAction(), noButton);\n            }\n        }\n        if (slot == storage.actionIndex()) {\n            final Button actionButton = storage.dialog().actionButton();\n            if (actionButton != null) {\n                clickButton(connection, storage.dialog().afterAction(), actionButton);\n            }\n        }\n\n        // Resync inventory view if the actions above didn't close the dialog nor opened another one.\n        final ChestDialogStorage currentStorage = connection.get(ChestDialogStorage.class);\n        if (currentStorage == storage && connection.has(ChestDialogStorage.class) && storage.phase() == ChestDialogStorage.Phase.DIALOG_VIEW) {\n            updateDialog(connection, storage.dialog());\n        }\n        return true;\n    }\n\n    public void updateDialog(final UserConnection connection, final Dialog dialog) {\n        final ChestDialogStorage storage = connection.get(ChestDialogStorage.class);\n\n        final PacketWrapper containerSetContent = PacketWrapper.create(ClientboundPackets1_21_5.CONTAINER_SET_CONTENT, connection);\n        containerSetContent.write(Types.VAR_INT, storage.containerId());\n        containerSetContent.write(Types.VAR_INT, 0); // Revision\n        containerSetContent.write(VersionedTypes.V1_21_5.itemArray, getItems(connection, storage, dialog));\n        containerSetContent.write(VersionedTypes.V1_21_5.item, StructuredItem.empty());\n        containerSetContent.send(Protocol1_21_6To1_21_5.class);\n    }\n\n    protected Item createPageNavigationItem() {\n        final DialogStyleConfig config = ViaBackwards.getConfig().dialogStyleConfig();\n\n        return createItem(\n            \"minecraft:arrow\",\n            translate(config.pageNavigationTitle()),\n\n            config.pageNavigationNext(),\n            config.pageNavigationPrevious()\n        );\n    }\n\n    protected Item createActionButtonItem(final UserConnection connection, final Button button) {\n        final Tag label = handleTag(connection, button.label());\n        if (button.tooltip() == null) {\n            return createItem(\"minecraft:oak_button\", label);\n        } else {\n            return createItem(\"minecraft:oak_button\", label, handleTag(connection, button.tooltip()));\n        }\n    }\n\n    protected Item createCloseButtonItem(final Tag label) {\n        return createItem(\"minecraft:oak_button\", label);\n    }\n\n    protected Item getItemWidget(final UserConnection connection, final ItemWidget itemWidget) {\n        final String identifier = itemWidget.item().getString(\"id\");\n        final int count = itemWidget.item().getInt(\"count\", 1);\n\n        final Tag label = translate(Key.stripMinecraftNamespace(identifier));\n        final Item item = createItem(identifier, label);\n        item.setAmount(count);\n        if (itemWidget.description() != null) {\n            item.dataContainer().set(StructuredDataKey.LORE, new Tag[]{\n                handleTag(connection, fixStyle(itemWidget.description().label()))\n            });\n        }\n        if (!itemWidget.showTooltip()) {\n            item.dataContainer().set(StructuredDataKey.TOOLTIP_DISPLAY, new TooltipDisplay(true, IntSortedSets.EMPTY_SET));\n        }\n        // If we were to parse item components from NBT for chat items they would be parsed here and stored into the data container.\n        // In VV, chat items are rewritten manually at the time being and therefore no conversion code exists.\n        return item;\n    }\n\n    protected Item getMultiTextWidget(final UserConnection connection, final MultiTextWidget multiTextWidget) {\n        final List<Tag> lines = new ArrayList<>();\n        for (final Tag label : multiTextWidget.labels()) {\n            final Tag[] split = ChatUtil.split(fixStyle(label), \"\\n\");\n            for (final Tag line : split) {\n                lines.add(handleTag(connection, line));\n            }\n        }\n\n        final Tag[] lore = new Tag[lines.size() - 1];\n        for (int i = 1; i < lines.size(); i++) {\n            lore[i - 1] = lines.get(i);\n        }\n        return createItem(\"minecraft:paper\", lines.get(0), lore);\n    }\n\n    protected Item getBooleanInput(final UserConnection connection, final BooleanInput booleanInput) {\n        final DialogStyleConfig config = ViaBackwards.getConfig().dialogStyleConfig();\n\n        final String item = booleanInput.value() ? \"minecraft:lime_dye\" : \"minecraft:gray_dye\";\n        final Tag[] label = ChatUtil.split(booleanInput.label(), \"\\n\");\n\n        // The only one that supports newlines in the label\n        if (label.length == 1) {\n            return createItem(\n                item,\n                handleTag(connection, booleanInput.label()),\n                translate(config.toggleValue())\n            );\n        } else {\n            final Tag[] lore = new Tag[label.length];\n            for (int i = 1; i < label.length; i++) {\n                lore[i - 1] = handleTag(connection, fixStyle(label[i]));\n            }\n            lore[lore.length - 1] = translate(config.toggleValue());\n            return createItem(\n                item,\n                handleTag(connection, label[0]),\n                lore\n            );\n        }\n    }\n\n    protected void clickBooleanInput(final BooleanInput booleanInput) {\n        booleanInput.setValue(!booleanInput.value());\n    }\n\n    protected Item getNumberRangeInput(final UserConnection connection, final NumberRangeInput numberRangeInput) {\n        final DialogStyleConfig config = ViaBackwards.getConfig().dialogStyleConfig();\n\n        final Tag label = handleTag(connection, numberRangeInput.displayName());\n        return createItem(\n            \"minecraft:clock\",\n            label,\n\n            String.format(config.increaseValue(), numberRangeInput.step()),\n            String.format(config.decreaseValue(), numberRangeInput.step()),\n            String.format(config.valueRange(), numberRangeInput.start(), numberRangeInput.end())\n        );\n    }\n\n    protected void clickNumberRangeInput(final NumberRangeInput numberRangeInput, final int mouse) {\n        float value = numberRangeInput.value();\n        if (mouse == 0) { // Left click\n            value += numberRangeInput.step();\n        } else if (mouse == 1) { // Right click\n            value -= numberRangeInput.step();\n        }\n        numberRangeInput.setClampedValue(value);\n    }\n\n    protected Item getTextInput(final UserConnection connection, final TextInput textInput) {\n        final DialogStyleConfig config = ViaBackwards.getConfig().dialogStyleConfig();\n\n        final Tag currentValue = translate(String.format(config.currentValue(), textInput.value()));\n        if (textInput.label() == null) {\n            return createItem(\"minecraft:writable_book\", currentValue);\n        } else {\n            final Tag label = handleTag(connection, textInput.label());\n            return createItem(\"minecraft:writable_book\", label, currentValue, translate(config.editValue()));\n        }\n    }\n\n    protected void clickTextInput(final UserConnection connection, final TextInput textInput) {\n        final ChestDialogStorage storage = connection.get(ChestDialogStorage.class);\n        openAnvilView(connection, storage, translate(\"§7Edit text\"), textInput.value(), textInput);\n    }\n\n    protected Item getSingleOptionInput(final UserConnection connection, final SingleOptionInput singleOptionInput) {\n        final DialogStyleConfig config = ViaBackwards.getConfig().dialogStyleConfig();\n\n        final Tag displayName = singleOptionInput.options()[singleOptionInput.value()].computeDisplay();\n        final Tag label;\n        if (singleOptionInput.label() != null) {\n            label = translate(\"options.generic_value\", singleOptionInput.label(), displayName);\n        } else {\n            label = displayName;\n        }\n        return createItem(\n            \"minecraft:bookshelf\",\n            handleTag(connection, label),\n            config.nextOption(),\n            config.previousOption()\n        );\n    }\n\n    protected void clickSingleOptionInput(final SingleOptionInput singleOptionInput) {\n        singleOptionInput.setClampedValue(singleOptionInput.value() + 1);\n    }\n\n    protected Item getButton(final UserConnection connection, final Button button) {\n        return createItem(\"minecraft:oak_button\", handleTag(connection, button.label()));\n    }\n\n    public void clickButton(final UserConnection connection, final Dialog.AfterAction afterAction, @Nullable final Button button) {\n        final ChestDialogStorage storage = connection.get(ChestDialogStorage.class);\n        switch (afterAction) {\n            case CLOSE -> closeDialog(connection);\n            case WAIT_FOR_RESPONSE -> storage.setPhase(null, ChestDialogStorage.Phase.WAITING_FOR_RESPONSE);\n        }\n\n        if (button == null || button.clickEvent() == null) {\n            return;\n        }\n\n        final CompoundTag clickEvent = button.clickEvent();\n        final String action = Key.stripMinecraftNamespace(clickEvent.getString(\"action\"));\n        switch (action) {\n            case \"open_url\" -> {\n                // We can't open a URL for the client, so roughly emulate by opening an Anvil containing the URL.\n                final String url = clickEvent.getString(\"url\");\n                openAnvilView(connection, storage, translate(\"Open URL\"), url, null);\n            }\n            case \"run_command\" -> {\n                // The vanilla client validates for signed argument types and has more requirements for this click event,\n                // but we can't do this here and therefore just always send the packet assuming this is correct...\n                final PacketWrapper chatCommand = PacketWrapper.create(ServerboundPackets1_21_6.CHAT_COMMAND, connection);\n                String command = clickEvent.getString(\"command\");\n                if (command.startsWith(\"/\")) {\n                    command = command.substring(1);\n                }\n                chatCommand.write(Types.STRING, command);\n                chatCommand.sendToServer(Protocol1_21_6To1_21_5.class);\n            }\n            case \"copy_to_clipboard\" -> {\n                // Same as above, we can't access the clipboard\n                final String value = clickEvent.getString(\"value\");\n                openAnvilView(connection, storage, translate(\"Copy to clipboard\"), value, null);\n            }\n        }\n\n        ClickEvents.handleClickEvent(connection, clickEvent); // Handle show_dialog and custom\n    }\n\n    protected Item createTextInputItem(final String value) {\n        final DialogStyleConfig config = ViaBackwards.getConfig().dialogStyleConfig();\n        return createItem(\"minecraft:paper\", translate(value), config.setText());\n    }\n\n    protected Item createTextCopyItem(final String value) {\n        final DialogStyleConfig config = ViaBackwards.getConfig().dialogStyleConfig();\n        return createItem(\"minecraft:paper\", translate(value), config.close());\n    }\n\n    protected void openAnvilView(\n        final UserConnection connection,\n        final ChestDialogStorage storage,\n        final Tag title,\n        final String value,\n        final TextInput textInput\n    ) {\n        storage.setPhase(connection, ChestDialogStorage.Phase.ANVIL_VIEW);\n\n        final PacketWrapper openScreen = PacketWrapper.create(ClientboundPackets1_21_5.OPEN_SCREEN, connection);\n        openScreen.write(Types.VAR_INT, storage.containerId());\n        openScreen.write(Types.VAR_INT, 8); // Container type id\n        openScreen.write(Types.TRUSTED_TAG, title);\n        openScreen.send(Protocol1_21_6To1_21_5.class);\n\n        final Item[] items = new Item[1];\n        items[0] = textInput != null ? createTextInputItem(value) : createTextCopyItem(value);\n        storage.setCurrentTextInput(textInput);\n\n        final PacketWrapper containerSetContent = PacketWrapper.create(ClientboundPackets1_21_5.CONTAINER_SET_CONTENT, connection);\n        containerSetContent.write(Types.VAR_INT, storage.containerId());\n        containerSetContent.write(Types.VAR_INT, 0); // Revision\n        containerSetContent.write(VersionedTypes.V1_21_5.itemArray, items);\n        containerSetContent.write(VersionedTypes.V1_21_5.item, StructuredItem.empty());\n        containerSetContent.send(Protocol1_21_6To1_21_5.class);\n    }\n\n    public void updateAnvilText(final UserConnection connection, final String value) {\n        if (value.isEmpty()) {\n            return;\n        }\n\n        final ChestDialogStorage storage = connection.get(ChestDialogStorage.class);\n        if (storage.currentTextInput() != null) {\n            storage.currentTextInput().setClampedValue(value);\n        }\n    }\n\n    protected Item getDialog(final UserConnection connection, final Dialog dialog) {\n        final Tag title = dialog.externalTitle() != null ? dialog.externalTitle() : dialog.title();\n        return createItem(\"minecraft:command_block\", handleTag(connection, title));\n    }\n\n    protected void clickDialogButton(final UserConnection connection, final Dialog dialog) {\n        closeDialog(connection);\n        openDialog(connection, dialog);\n    }\n\n    protected Item getItem(final UserConnection connection, final Widget widget) {\n        if (widget instanceof final ItemWidget itemWidget) {\n            return getItemWidget(connection, itemWidget);\n        } else if (widget instanceof final MultiTextWidget multiTextWidget) {\n            return getMultiTextWidget(connection, multiTextWidget);\n        } else if (widget instanceof final BooleanInput booleanInput) {\n            return getBooleanInput(connection, booleanInput);\n        } else if (widget instanceof final NumberRangeInput numberRangeInput) {\n            return getNumberRangeInput(connection, numberRangeInput);\n        } else if (widget instanceof TextInput textInput) {\n            return getTextInput(connection, textInput);\n        } else if (widget instanceof SingleOptionInput singleOptionInput) {\n            return getSingleOptionInput(connection, singleOptionInput);\n        } else if (widget instanceof Button button) {\n            return getButton(connection, button);\n        } else if (widget instanceof Dialog dialog) {\n            return getDialog(connection, dialog);\n        }\n\n        throw new IllegalArgumentException(\"Unknown widget type: \" + widget.getClass().getName());\n    }\n\n    protected Item[] getItems(final UserConnection connection, final ChestDialogStorage storage, final Dialog dialog) {\n        final Item[] items = StructuredItem.emptyArray(INVENTORY_SIZE);\n        int confirmationYesIndex = -1;\n        int confirmationNoIndex = -1;\n        int actionIndex = -1;\n\n        if (storage.phase() == ChestDialogStorage.Phase.WAITING_FOR_RESPONSE) {\n            actionIndex = 13;\n\n            items[actionIndex] = createCloseButtonItem(storage.closeButtonLabel());\n            storage.setItems(items, confirmationYesIndex, confirmationNoIndex, actionIndex);\n            return items;\n        }\n\n        final List<Widget> widgets = dialog.widgets();\n        if (widgets.size() > INVENTORY_LAST_ROW) {\n            final int begin = storage.page * INVENTORY_LAST_ROW;\n            final int end = Math.min((storage.page + 1) * INVENTORY_LAST_ROW, widgets.size());\n            for (int i = 0; i < end - begin; i++) {\n                items[i] = getItem(connection, widgets.get(begin + i));\n            }\n\n            items[INVENTORY_SIZE - 1] = createPageNavigationItem();\n        } else {\n            for (int i = 0; i < widgets.size(); i++) {\n                items[i] = getItem(connection, widgets.get(i));\n            }\n        }\n\n        // And some special cases.\n        if (dialog.yesButton() != null && dialog.noButton() != null) {\n            confirmationYesIndex = widgets.isEmpty() ? 11 : INVENTORY_SIZE - 7;\n            confirmationNoIndex = widgets.isEmpty() ? 15 : INVENTORY_SIZE - 3;\n\n            items[confirmationYesIndex] = createActionButtonItem(connection, dialog.yesButton());\n            items[confirmationNoIndex] = createActionButtonItem(connection, dialog.noButton());\n        }\n        if (dialog.actionButton() != null) {\n            actionIndex = widgets.isEmpty() ? 13 : INVENTORY_SIZE - 9;\n\n            items[actionIndex] = createActionButtonItem(connection, dialog.actionButton());\n        }\n\n        storage.setItems(items, confirmationYesIndex, confirmationNoIndex, actionIndex);\n        return items;\n    }\n\n    /**\n     * Handles the 1.21.6 tag inside the Dialog structure and rewrites it to the 1.21.5 format. This is necessary\n     * for replacing translations used by Dialogs which only exist in 1.21.6+. Call this function for every text component\n     * you display from the server.\n     *\n     * @param connection the user connection\n     * @param tag        the text component as a tag, or null if no tag is present\n     * @return the rewritten tag, or null if the input tag was null\n     */\n    protected @Nullable Tag handleTag(final UserConnection connection, final @Nullable Tag tag) {\n        if (tag == null) {\n            return null;\n        }\n\n        protocol.getComponentRewriter().processTag(connection, tag);\n        return tag;\n    }\n\n    /**\n     * This doesn't actually exist in Minecraft but is created if multiple {@link TextWidget} follow one another in order\n     * to improve readability in chest inventories.\n     */\n    public record MultiTextWidget(Tag[] labels) implements Widget {\n\n    }\n\n    // -------------------------------------------------------------------------------------\n\n    protected Item createItem(final String identifier, final Tag name) {\n        return createItem(identifier, name, new String[0]);\n    }\n\n    protected Item createItem(final String identifier, final Tag name, final Tag... description) {\n        final int id = protocol.getMappingData().getFullItemMappings().mappedId(identifier);\n\n        final StructuredDataContainer data = new StructuredDataContainer();\n        data.setIdLookup(protocol, true);\n        data.set(StructuredDataKey.ITEM_NAME, name);\n        if (description != null) {\n            data.set(StructuredDataKey.LORE, description);\n        }\n        return new StructuredItem(id, 1, data);\n    }\n\n    protected Item createItem(final String identifier, final Tag name, final String... description) {\n        final int id = protocol.getMappingData().getFullItemMappings().mappedId(identifier);\n\n        final StructuredDataContainer data = new StructuredDataContainer();\n        data.setIdLookup(protocol, true);\n        data.set(StructuredDataKey.ITEM_NAME, name);\n        if (description.length > 0) {\n            final List<Tag> lore = new ArrayList<>();\n            for (final String s : description) {\n                lore.add(translate(s));\n            }\n            data.set(StructuredDataKey.LORE, lore.toArray(new Tag[0]));\n        }\n        return new StructuredItem(id, 1, data);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_6to1_21_5/provider/DialogViewProvider.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.provider;\n\nimport com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.data.Dialog;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.platform.providers.Provider;\n\n/**\n * Interface for providing dialog view functionality to this protocol. Requires a storage\n * to save per-user data while being able to have open and close logic static.\n * <p>\n * See {@link Dialog} for the structure of a dialog.\n * <p>\n * See {@link ChestDialogViewProvider} for the protocol level emulation using a chest.\n */\npublic interface DialogViewProvider extends Provider {\n\n    void openDialog(final UserConnection connection, final Dialog dialog);\n\n    void closeDialog(final UserConnection connection);\n\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_6to1_21_5/rewriter/BlockItemPacketRewriter1_21_6.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.rewriter;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.ListTag;\nimport com.viaversion.viabackwards.api.rewriters.BackwardsStructuredItemRewriter;\nimport com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.Protocol1_21_6To1_21_5;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.data.StructuredDataContainer;\nimport com.viaversion.viaversion.api.minecraft.data.StructuredDataKey;\nimport com.viaversion.viaversion.api.minecraft.item.Item;\nimport com.viaversion.viaversion.api.minecraft.item.data.AttributeModifiers1_21;\nimport com.viaversion.viaversion.api.minecraft.item.data.Equippable;\nimport com.viaversion.viaversion.protocols.v1_21_4to1_21_5.packet.ServerboundPacket1_21_5;\nimport com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ClientboundPacket1_21_6;\n\nimport static com.viaversion.viaversion.protocols.v1_21_5to1_21_6.rewriter.BlockItemPacketRewriter1_21_6.downgradeItemData;\nimport static com.viaversion.viaversion.protocols.v1_21_5to1_21_6.rewriter.BlockItemPacketRewriter1_21_6.upgradeItemData;\n\npublic final class BlockItemPacketRewriter1_21_6 extends BackwardsStructuredItemRewriter<ClientboundPacket1_21_6, ServerboundPacket1_21_5, Protocol1_21_6To1_21_5> {\n\n    public BlockItemPacketRewriter1_21_6(final Protocol1_21_6To1_21_5 protocol) {\n        super(protocol);\n    }\n\n    @Override\n    protected void handleItemDataComponentsToClient(final UserConnection connection, final Item item, final StructuredDataContainer container) {\n        downgradeItemData(item);\n        super.handleItemDataComponentsToClient(connection, item, container);\n    }\n\n    @Override\n    protected void handleItemDataComponentsToServer(final UserConnection connection, final Item item, final StructuredDataContainer container) {\n        upgradeItemData(item);\n        super.handleItemDataComponentsToServer(connection, item, container);\n    }\n\n    @Override\n    protected void backupInconvertibleData(final UserConnection connection, final Item item, final StructuredDataContainer dataContainer, final CompoundTag backupTag) {\n        super.backupInconvertibleData(connection, item, dataContainer, backupTag);\n        final AttributeModifiers1_21 attributeModifiers = dataContainer.get(StructuredDataKey.ATTRIBUTE_MODIFIERS1_21_6);\n        if (attributeModifiers != null) {\n            final ListTag<CompoundTag> modifiersBackup = new ListTag<>(CompoundTag.class);\n            boolean needsBackup = false;\n            for (final AttributeModifiers1_21.AttributeModifier modifier : attributeModifiers.modifiers()) {\n                if (modifier.display().id() != 0) {\n                    needsBackup = true;\n                }\n\n                final CompoundTag modifierBackup = new CompoundTag();\n                modifiersBackup.add(modifierBackup);\n                modifierBackup.putInt(\"id\", modifier.display().id());\n                if (modifier.display() instanceof AttributeModifiers1_21.OverrideText overrideText) {\n                    modifierBackup.put(\"text\", overrideText.component());\n                }\n            }\n            if (needsBackup) {\n                backupTag.put(\"attribute_modifiers_displays\", modifiersBackup);\n            }\n        }\n\n        final Equippable equippable = dataContainer.get(StructuredDataKey.EQUIPPABLE1_21_6);\n        if (equippable != null && equippable.canBeSheared()) {\n            final CompoundTag equippableTag = new CompoundTag();\n            equippableTag.putBoolean(\"can_be_sheared\", true);\n            saveSoundEventHolder(equippableTag, equippable.shearingSound());\n            backupTag.put(\"equippable\", equippableTag);\n        }\n    }\n\n    @Override\n    protected void restoreBackupData(final Item item, final StructuredDataContainer container, final CompoundTag customData) {\n        super.restoreBackupData(item, container, customData);\n        if (!(customData.remove(nbtTagName(\"backup\")) instanceof final CompoundTag backupTag)) {\n            return;\n        }\n\n        final ListTag<CompoundTag> attributeModifiersDisplays = backupTag.getListTag(\"attribute_modifiers_displays\", CompoundTag.class);\n        if (attributeModifiersDisplays != null) {\n            container.replace(StructuredDataKey.ATTRIBUTE_MODIFIERS1_21_5, StructuredDataKey.ATTRIBUTE_MODIFIERS1_21_6, modifiers -> {\n                final AttributeModifiers1_21.AttributeModifier[] updatedModifiers = new AttributeModifiers1_21.AttributeModifier[modifiers.modifiers().length];\n                for (int i = 0; i < modifiers.modifiers().length; i++) {\n                    final CompoundTag modifierBackup = attributeModifiersDisplays.get(i);\n                    final int id = modifierBackup.getInt(\"id\");\n                    final AttributeModifiers1_21.Display display = id == 2 ? new AttributeModifiers1_21.OverrideText(modifierBackup.get(\"text\")) : new AttributeModifiers1_21.Display(id);\n                    final AttributeModifiers1_21.AttributeModifier modifier = modifiers.modifiers()[i];\n                    updatedModifiers[i] = new AttributeModifiers1_21.AttributeModifier(modifier.attribute(), modifier.modifier(), modifier.slotType(), display);\n                }\n                return new AttributeModifiers1_21(updatedModifiers);\n            });\n        }\n\n        final CompoundTag equippableTag = backupTag.getCompoundTag(\"equippable\");\n        if (equippableTag != null) {\n            container.replace(StructuredDataKey.EQUIPPABLE1_21_5, StructuredDataKey.EQUIPPABLE1_21_6, equippable -> new Equippable(\n                equippable.equipmentSlot(), equippable.soundEvent(), equippable.model(), equippable.cameraOverlay(), equippable.allowedEntities(),\n                equippable.dispensable(), equippable.swappable(), equippable.damageOnHurt(), equippable.equipOnInteract(),\n                equippableTag.getBoolean(\"can_be_sheared\"),\n                restoreSoundEventHolder(equippableTag)\n            ));\n        }\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_6to1_21_5/rewriter/ComponentRewriter1_21_6.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.rewriter;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viabackwards.api.rewriters.text.NBTComponentRewriter;\nimport com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.storage.ClickEvents;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ClientboundPacket1_21_6;\n\npublic final class ComponentRewriter1_21_6 extends NBTComponentRewriter<ClientboundPacket1_21_6> {\n\n    public ComponentRewriter1_21_6(final BackwardsProtocol<ClientboundPacket1_21_6, ?, ?, ?> protocol) {\n        super(protocol);\n    }\n\n    @Override\n    protected void handleClickEvent(final UserConnection connection, final CompoundTag clickEventTag) {\n        final String action = clickEventTag.getString(\"action\");\n\n        // Make them run a command generated by us executing the actual logic\n        if (\"show_dialog\".equals(action)) {\n            final ClickEvents clickEvents = connection.get(ClickEvents.class);\n            final String command = clickEvents.storeClickEvent(clickEventTag.copy());\n\n            clickEventTag.putString(\"action\", \"run_command\");\n            clickEventTag.putString(\"command\", command);\n            clickEventTag.remove(\"dialog\");\n        } else if (\"custom\".equals(action)) {\n            final ClickEvents clickEvents = connection.get(ClickEvents.class);\n            final String command = clickEvents.storeClickEvent(clickEventTag.copy());\n\n            clickEventTag.putString(\"action\", \"run_command\");\n            clickEventTag.putString(\"command\", command);\n            clickEventTag.remove(\"id\");\n            clickEventTag.remove(\"payload\");\n        }\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_6to1_21_5/rewriter/EntityPacketRewriter1_21_6.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.rewriter;\n\nimport com.viaversion.viabackwards.api.rewriters.EntityRewriter;\nimport com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.Protocol1_21_6To1_21_5;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityType;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_21_6;\nimport com.viaversion.viaversion.api.minecraft.entitydata.types.EntityDataTypes1_21_5;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.version.VersionedTypes;\nimport com.viaversion.viaversion.protocols.v1_21_4to1_21_5.packet.ClientboundPackets1_21_5;\nimport com.viaversion.viaversion.protocols.v1_21_4to1_21_5.packet.ServerboundPackets1_21_5;\nimport com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ClientboundPacket1_21_6;\nimport com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ClientboundPackets1_21_6;\n\npublic final class EntityPacketRewriter1_21_6 extends EntityRewriter<ClientboundPacket1_21_6, Protocol1_21_6To1_21_5> {\n\n    public EntityPacketRewriter1_21_6(final Protocol1_21_6To1_21_5 protocol) {\n        super(protocol, VersionedTypes.V1_21_6.entityDataTypes.optionalComponentType, VersionedTypes.V1_21_6.entityDataTypes.booleanType);\n    }\n\n    @Override\n    public void registerPackets() {\n        protocol.replaceClientbound(ClientboundPackets1_21_6.ADD_ENTITY, wrapper -> {\n            final int entityId = wrapper.passthrough(Types.VAR_INT);\n            wrapper.passthrough(Types.UUID); // Entity UUID\n            final int entityType = wrapper.passthrough(Types.VAR_INT);\n            wrapper.passthrough(Types.DOUBLE); // X\n            wrapper.passthrough(Types.DOUBLE); // Y\n            wrapper.passthrough(Types.DOUBLE); // Z\n            wrapper.passthrough(Types.BYTE); // Pitch\n            wrapper.passthrough(Types.BYTE); // Yaw\n            wrapper.passthrough(Types.BYTE); // Head yaw\n            wrapper.passthrough(Types.VAR_INT); // Data\n            final short velocityX = wrapper.passthrough(Types.SHORT);\n            final short velocityY = wrapper.passthrough(Types.SHORT);\n            final short velocityZ = wrapper.passthrough(Types.SHORT);\n            getSpawnTrackerWithDataHandler1_19().handle(wrapper);\n            if (velocityX != 0 || velocityY != 0 || velocityZ != 0) {\n                if (!typeFromId(entityType).isOrHasParent(EntityTypes1_21_6.LIVING_ENTITY)) {\n                    // Send movement separately\n                    final PacketWrapper motionPacket = wrapper.create(ClientboundPackets1_21_5.SET_ENTITY_MOTION);\n                    motionPacket.write(Types.VAR_INT, entityId);\n                    motionPacket.write(Types.SHORT, velocityX);\n                    motionPacket.write(Types.SHORT, velocityY);\n                    motionPacket.write(Types.SHORT, velocityZ);\n                    wrapper.send(Protocol1_21_6To1_21_5.class);\n                    motionPacket.send(Protocol1_21_6To1_21_5.class);\n                    wrapper.cancel();\n                }\n            }\n        });\n\n        protocol.registerServerbound(ServerboundPackets1_21_5.PLAYER_COMMAND, wrapper -> {\n            wrapper.passthrough(Types.VAR_INT); // Entity ID\n\n            // press_shift_key and release_shift_key gone. The server uses (the already sent) player input instead\n            final int action = wrapper.read(Types.VAR_INT);\n            if (action < 2) {\n                wrapper.cancel();\n            }\n\n            wrapper.write(Types.VAR_INT, action - 2);\n        });\n    }\n\n    @Override\n    protected void registerRewrites() {\n        final EntityDataTypes1_21_5 entityDataTypes = VersionedTypes.V1_21_5.entityDataTypes;\n        dataTypeMapper().register();\n        registerEntityDataTypeHandler1_20_3(\n            entityDataTypes.itemType,\n            entityDataTypes.blockStateType,\n            entityDataTypes.optionalBlockStateType,\n            entityDataTypes.particleType,\n            entityDataTypes.particlesType,\n            entityDataTypes.componentType,\n            entityDataTypes.optionalComponentType\n        );\n\n        filter().type(EntityTypes1_21_6.HANGING_ENTITY).removeIndex(8); // Direction\n        filter().type(EntityTypes1_21_6.HAPPY_GHAST).cancel(17); // Leash holder\n        filter().type(EntityTypes1_21_6.HAPPY_GHAST).cancel(18); // Stays still\n    }\n\n    @Override\n    public void onMappingDataLoaded() {\n        super.onMappingDataLoaded();\n        mapEntityTypeWithData(EntityTypes1_21_6.HAPPY_GHAST, EntityTypes1_21_6.GHAST).tagName();\n    }\n\n    @Override\n    public EntityType typeFromId(final int type) {\n        return EntityTypes1_21_6.getTypeFromId(type);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_6to1_21_5/rewriter/RegistryDataRewriter1_21_6.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.rewriter;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viabackwards.api.rewriters.BackwardsRegistryRewriter;\nimport com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.storage.RegistryAndTags;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.RegistryEntry;\nimport com.viaversion.viaversion.libs.fastutil.objects.Object2ObjectArrayMap;\nimport com.viaversion.viaversion.libs.fastutil.objects.Object2ObjectMap;\nimport com.viaversion.viaversion.util.Key;\nimport com.viaversion.viaversion.util.KeyMappings;\n\npublic final class RegistryDataRewriter1_21_6 extends BackwardsRegistryRewriter {\n\n    public RegistryDataRewriter1_21_6(final BackwardsProtocol<?, ?, ?, ?> protocol) {\n        super(protocol);\n\n        remove(\"dialog\"); // Tracked and now removed\n    }\n\n    @Override\n    public RegistryEntry[] handle(final UserConnection connection, final String key, final RegistryEntry[] entries) {\n        if (Key.stripMinecraftNamespace(key).equals(\"dialog\")) {\n            final String[] keys = new String[entries.length];\n            for (int i = 0; i < entries.length; i++) {\n                keys[i] = Key.stripMinecraftNamespace(entries[i].key());\n            }\n\n            final Object2ObjectMap<String, CompoundTag> dialogs = new Object2ObjectArrayMap<>();\n            for (final RegistryEntry entry : entries) {\n                if (entry.tag() instanceof final CompoundTag tag) {\n                    dialogs.put(Key.stripMinecraftNamespace(entry.key()), tag);\n                }\n            }\n\n            final RegistryAndTags registryAndTags = connection.get(RegistryAndTags.class);\n            registryAndTags.storeRegistry(new KeyMappings(keys), dialogs);\n        }\n        return super.handle(connection, key, entries);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_6to1_21_5/storage/ChestDialogStorage.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.storage;\n\nimport com.viaversion.nbt.tag.Tag;\nimport com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.Protocol1_21_6To1_21_5;\nimport com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.data.Dialog;\nimport com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.data.input.TextInput;\nimport com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.provider.ChestDialogViewProvider;\nimport com.viaversion.viaversion.api.connection.StorableObject;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.item.Item;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.protocols.v1_21_4to1_21_5.packet.ClientboundPackets1_21_5;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport org.checkerframework.checker.nullness.qual.Nullable;\n\nimport static com.viaversion.viabackwards.utils.ChatUtil.translate;\n\n/**\n * Per-user storage for {@link ChestDialogViewProvider}\n */\npublic final class ChestDialogStorage implements StorableObject {\n\n    private static final byte MIN_FAKE_ID = Byte.MAX_VALUE / 2 + 1;\n    private static final byte MAX_FAKE_ID = Byte.MAX_VALUE - 1;\n    private static final AtomicInteger FAKE_ID_COUNTER = new AtomicInteger(MIN_FAKE_ID);\n\n    private static final Tag[] RESPONSE_BUTTON_LABELS = new Tag[]{\n        translate(\"\"),\n        translate(\"gui.waitingForResponse.button.inactive\", 4),\n        translate(\"gui.waitingForResponse.button.inactive\", 3),\n        translate(\"gui.waitingForResponse.button.inactive\", 2),\n        translate(\"gui.waitingForResponse.button.inactive\", 1),\n        translate(\"gui.back\")\n    };\n\n    private final ChestDialogViewProvider provider;\n    private final Dialog dialog;\n    private int containerId;\n    private Dialog previousDialog;\n\n    public int page;\n\n    private Item[] items;\n    private int confirmationYesIndex = -1;\n    private int confirmationNoIndex = -1;\n\n    private int actionIndex = -1;\n\n    private @Nullable Phase phase;\n\n    private int ticksWaitingForResponse = 0;\n\n    private boolean closeButtonEnabled;\n    private Tag closeButtonLabel;\n\n    private TextInput currentTextInput;\n\n    private boolean allowClosing;\n\n    public ChestDialogStorage(final ChestDialogViewProvider provider, final Dialog dialog) {\n        this.provider = provider;\n        this.dialog = dialog;\n    }\n\n    public void tick(final UserConnection connection) {\n        if (phase != Phase.WAITING_FOR_RESPONSE) {\n            return;\n        }\n\n        final int index = ticksWaitingForResponse++ / 20;\n        if (index > RESPONSE_BUTTON_LABELS.length - 1) {\n            return;\n        }\n\n        closeButtonEnabled = index >= 1;\n        closeButtonLabel = RESPONSE_BUTTON_LABELS[index];\n        provider.updateDialog(connection, dialog);\n    }\n\n    public Dialog dialog() {\n        return dialog;\n    }\n\n    public @Nullable Dialog previousDialog() {\n        return previousDialog;\n    }\n\n    public void setPreviousDialog(final Dialog previousDialog) {\n        this.previousDialog = previousDialog;\n    }\n\n    public int containerId() {\n        return containerId;\n    }\n\n    public Item[] items() {\n        return items;\n    }\n\n    public void setItems(final Item[] items, final int confirmationYesIndex, final int confirmationNoIndex, final int actionIndex) {\n        this.items = items;\n        this.confirmationYesIndex = confirmationYesIndex;\n        this.confirmationNoIndex = confirmationNoIndex;\n        this.actionIndex = actionIndex;\n    }\n\n    public int confirmationYesIndex() {\n        return confirmationYesIndex;\n    }\n\n    public int confirmationNoIndex() {\n        return confirmationNoIndex;\n    }\n\n    public int actionIndex() {\n        return actionIndex;\n    }\n\n    public @Nullable Phase phase() {\n        return phase;\n    }\n\n    public boolean closeButtonEnabled() {\n        return closeButtonEnabled;\n    }\n\n    public Tag closeButtonLabel() {\n        return closeButtonLabel;\n    }\n\n    public TextInput currentTextInput() {\n        return currentTextInput;\n    }\n\n    public boolean allowClosing() {\n        return allowClosing;\n    }\n\n    public void setPhase(final UserConnection connection, final @Nullable Phase phase) {\n        if (phase == Phase.DIALOG_VIEW || phase == Phase.ANVIL_VIEW) {\n            if (this.phase != null) {\n                final PacketWrapper containerClose = PacketWrapper.create(ClientboundPackets1_21_5.CONTAINER_CLOSE, connection);\n                containerClose.write(Types.VAR_INT, containerId);\n                containerClose.send(Protocol1_21_6To1_21_5.class);\n            }\n            currentTextInput = null;\n\n            final int id = FAKE_ID_COUNTER.getAndIncrement();\n            if (id > MAX_FAKE_ID) {\n                FAKE_ID_COUNTER.set(MIN_FAKE_ID);\n                containerId = MIN_FAKE_ID;\n            } else {\n                containerId = (byte) id;\n            }\n        }\n\n        this.phase = phase;\n    }\n\n    public void setCurrentTextInput(final TextInput currentTextInput) {\n        this.currentTextInput = currentTextInput;\n    }\n\n    public void setAllowClosing(final boolean allowClosing) {\n        this.allowClosing = allowClosing;\n    }\n\n    public enum Phase {\n\n        DIALOG_VIEW,\n        ANVIL_VIEW,\n        WAITING_FOR_RESPONSE\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_6to1_21_5/storage/ClickEvents.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.storage;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.StringTag;\nimport com.viaversion.viabackwards.ViaBackwards;\nimport com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.Protocol1_21_6To1_21_5;\nimport com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.data.Dialog;\nimport com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.provider.DialogViewProvider;\nimport com.viaversion.viaversion.api.Via;\nimport com.viaversion.viaversion.api.connection.StorableObject;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.libs.fastutil.objects.Object2ObjectArrayMap;\nimport com.viaversion.viaversion.libs.fastutil.objects.Object2ObjectMap;\nimport com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ServerboundPackets1_21_6;\nimport com.viaversion.viaversion.util.Key;\nimport java.util.UUID;\n\npublic final class ClickEvents implements StorableObject {\n\n    private final Object2ObjectMap<String, CompoundTag> clickEvents = new Object2ObjectArrayMap<>();\n\n    public String storeClickEvent(final CompoundTag clickEvent) {\n        final String id = \"vv_\" + UUID.randomUUID();\n        if (clickEvents.containsKey(id)) {\n            return storeClickEvent(clickEvent); // Ensure unique ID\n        }\n        clickEvents.put(id, clickEvent);\n        return id;\n    }\n\n    public boolean handleChatCommand(final UserConnection connection, final String command) {\n        final CompoundTag clickEvent = clickEvents.get(command);\n        if (clickEvent != null) {\n            handleClickEvent(connection, clickEvent);\n            return true;\n        } else {\n            return false;\n        }\n    }\n\n    public static void handleClickEvent(final UserConnection connection, final CompoundTag clickEvent) {\n        final String action = Key.stripMinecraftNamespace(clickEvent.getString(\"action\"));\n        if (\"show_dialog\".equals(action)) {\n            if (!ViaBackwards.getConfig().dialogsViaChests()) {\n                return;\n            }\n\n            final RegistryAndTags registryAndTags = connection.get(RegistryAndTags.class);\n            final ServerLinks serverLinks = connection.get(ServerLinks.class);\n\n            CompoundTag dialogTag = clickEvent.getCompoundTag(\"dialog\");\n            if (dialogTag == null) {\n                final StringTag dialogReferenceTag = clickEvent.getStringTag(\"dialog\");\n                if (dialogReferenceTag != null) { // No tags here\n                    dialogTag = registryAndTags.fromRegistry(Key.stripMinecraftNamespace(dialogReferenceTag.getValue()));\n                }\n            }\n\n            final DialogViewProvider provider = Via.getManager().getProviders().get(DialogViewProvider.class);\n            provider.openDialog(connection, new Dialog(registryAndTags, serverLinks, dialogTag));\n        } else if (\"custom\".equals(action)) {\n            final String id = clickEvent.getString(\"id\");\n            final CompoundTag payload = clickEvent.getCompoundTag(\"payload\");\n\n            final PacketWrapper customClickAction = PacketWrapper.create(ServerboundPackets1_21_6.CUSTOM_CLICK_ACTION, connection);\n            customClickAction.write(Types.STRING, id);\n            customClickAction.write(Types.CUSTOM_CLICK_ACTION_TAG, payload);\n\n            customClickAction.sendToServer(Protocol1_21_6To1_21_5.class);\n        }\n    }\n\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_6to1_21_5/storage/RegistryAndTags.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.storage;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.viaversion.api.connection.StorableObject;\nimport com.viaversion.viaversion.libs.fastutil.objects.Object2ObjectArrayMap;\nimport com.viaversion.viaversion.libs.fastutil.objects.Object2ObjectMap;\nimport com.viaversion.viaversion.util.Key;\nimport com.viaversion.viaversion.util.KeyMappings;\n\npublic final class RegistryAndTags implements StorableObject {\n\n    private KeyMappings dialogMappings;\n    private Object2ObjectMap<String, CompoundTag> dialogs;\n\n    private Object2ObjectMap<String, int[]> dialogTags;\n\n    public void storeRegistry(final KeyMappings dialogMappings, final Object2ObjectMap<String, CompoundTag> dialogs) {\n        this.dialogMappings = dialogMappings;\n        this.dialogs = dialogs;\n    }\n\n    public CompoundTag fromRegistry(final int id) {\n        return dialogs.get(dialogMappings.idToKey(id));\n    }\n\n    public CompoundTag fromRegistry(final String key) {\n        return dialogs.get(Key.stripMinecraftNamespace(key));\n    }\n\n    public boolean tagsSent() {\n        return dialogTags != null && !dialogTags.isEmpty();\n    }\n\n    public void storeTags(final String key, final int[] ids) {\n        if (dialogTags == null) {\n            dialogTags = new Object2ObjectArrayMap<>();\n        }\n        dialogTags.put(Key.stripMinecraftNamespace(key), ids);\n    }\n\n    public int[] fromKey(final String key) {\n        return dialogTags.get(Key.stripMinecraftNamespace(key));\n    }\n\n    public CompoundTag[] fromRegistryKey(final String key) {\n        final int[] ids = fromKey(key);\n        if (ids == null) {\n            return null;\n        }\n\n        final CompoundTag[] tags = new CompoundTag[ids.length];\n        for (int i = 0; i < ids.length; i++) {\n            tags[i] = fromRegistry(ids[i]);\n        }\n        return tags;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_6to1_21_5/storage/ServerLinks.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.storage;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.Tag;\nimport com.viaversion.viaversion.api.connection.StorableObject;\nimport com.viaversion.viaversion.libs.fastutil.objects.Object2ObjectArrayMap;\nimport com.viaversion.viaversion.libs.fastutil.objects.Object2ObjectMap;\n\nimport static com.viaversion.viabackwards.utils.ChatUtil.translate;\n\npublic final class ServerLinks implements StorableObject {\n\n    private static final CompoundTag REPORT_BUG = translate(\"known_server_link.report_bug\");\n    private static final CompoundTag COMMUNITY_GUIDELINES = translate(\"known_server_link.community_guidelines\");\n    private static final CompoundTag SUPPORT = translate(\"known_server_link.support\");\n    private static final CompoundTag STATUS = translate(\"known_server_link.status\");\n    private static final CompoundTag FEEDBACK = translate(\"known_server_link.feedback\");\n    private static final CompoundTag COMMUNITY = translate(\"known_server_link.community\");\n    private static final CompoundTag WEBSITE = translate(\"known_server_link.website\");\n    private static final CompoundTag FORUMS = translate(\"known_server_link.forums\");\n    private static final CompoundTag NEWS = translate(\"known_server_link.news\");\n    private static final CompoundTag ANNOUNCEMENTS = translate(\"known_server_link.announcements\");\n\n    private final Object2ObjectMap<Tag, String> links = new Object2ObjectArrayMap<>();\n\n    public void storeLink(final Tag tag, final String uri) {\n        links.put(tag, uri);\n    }\n\n    public void storeLink(final int id, final String uri) {\n        switch (id) {\n            case 1 -> storeLink(COMMUNITY_GUIDELINES, uri);\n            case 2 -> storeLink(SUPPORT, uri);\n            case 3 -> storeLink(STATUS, uri);\n            case 4 -> storeLink(FEEDBACK, uri);\n            case 5 -> storeLink(COMMUNITY, uri);\n            case 6 -> storeLink(WEBSITE, uri);\n            case 7 -> storeLink(FORUMS, uri);\n            case 8 -> storeLink(NEWS, uri);\n            case 9 -> storeLink(ANNOUNCEMENTS, uri);\n            default -> storeLink(REPORT_BUG, uri);\n        }\n    }\n\n    public Object2ObjectMap<Tag, String> links() {\n        return links;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_6to1_21_5/task/ChestDialogViewTask.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.task;\n\nimport com.viaversion.viabackwards.protocol.v1_21_6to1_21_5.storage.ChestDialogStorage;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.connection.StorableObjectTask;\n\npublic final class ChestDialogViewTask extends StorableObjectTask<ChestDialogStorage> {\n\n    public ChestDialogViewTask() {\n        super(ChestDialogStorage.class);\n    }\n\n    @Override\n    public void run(final UserConnection userConnection, final ChestDialogStorage storableObject) {\n        storableObject.tick(userConnection);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_7to1_21_6/Protocol1_21_7To1_21_6.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_7to1_21_6;\n\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viabackwards.api.data.BackwardsMappingData;\nimport com.viaversion.viabackwards.api.rewriters.BackwardsRegistryRewriter;\nimport com.viaversion.viabackwards.api.rewriters.text.NBTComponentRewriter;\nimport com.viaversion.viabackwards.protocol.v1_21_7to1_21_6.rewriter.BlockItemPacketRewriter1_21_7;\nimport com.viaversion.viabackwards.protocol.v1_21_7to1_21_6.rewriter.EntityPacketRewriter1_21_7;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.data.version.StructuredDataKeys1_21_5;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_21_6;\nimport com.viaversion.viaversion.api.minecraft.entitydata.types.EntityDataTypes1_21_5;\nimport com.viaversion.viaversion.api.protocol.packet.provider.PacketTypesProvider;\nimport com.viaversion.viaversion.api.protocol.packet.provider.SimplePacketTypesProvider;\nimport com.viaversion.viaversion.api.type.types.chunk.ChunkType1_21_5;\nimport com.viaversion.viaversion.api.type.types.version.Types1_20_5;\nimport com.viaversion.viaversion.api.type.types.version.VersionedTypes;\nimport com.viaversion.viaversion.data.entity.EntityTrackerBase;\nimport com.viaversion.viaversion.data.item.ItemHasherBase;\nimport com.viaversion.viaversion.protocols.v1_21_4to1_21_5.rewriter.RecipeDisplayRewriter1_21_5;\nimport com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ClientboundConfigurationPackets1_21_6;\nimport com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ClientboundPacket1_21_6;\nimport com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ClientboundPackets1_21_6;\nimport com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ServerboundConfigurationPackets1_21_6;\nimport com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ServerboundPacket1_21_6;\nimport com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ServerboundPackets1_21_6;\nimport com.viaversion.viaversion.protocols.v1_21_6to1_21_7.Protocol1_21_6To1_21_7;\nimport com.viaversion.viaversion.rewriter.BlockRewriter;\nimport com.viaversion.viaversion.rewriter.ParticleRewriter;\nimport com.viaversion.viaversion.rewriter.RecipeDisplayRewriter;\nimport com.viaversion.viaversion.rewriter.TagRewriter;\n\nimport static com.viaversion.viaversion.util.ProtocolUtil.packetTypeMap;\n\npublic final class Protocol1_21_7To1_21_6 extends BackwardsProtocol<ClientboundPacket1_21_6, ClientboundPacket1_21_6, ServerboundPacket1_21_6, ServerboundPacket1_21_6> {\n\n    public static final BackwardsMappingData MAPPINGS = new BackwardsMappingData(\"1.21.7\", \"1.21.6\", Protocol1_21_6To1_21_7.class);\n    private final EntityPacketRewriter1_21_7 entityRewriter = new EntityPacketRewriter1_21_7(this);\n    private final BlockItemPacketRewriter1_21_7 itemRewriter = new BlockItemPacketRewriter1_21_7(this);\n    private final ParticleRewriter<ClientboundPacket1_21_6> particleRewriter = new ParticleRewriter<>(this);\n    private final NBTComponentRewriter<ClientboundPacket1_21_6> translatableRewriter = new NBTComponentRewriter<>(this);\n    private final TagRewriter<ClientboundPacket1_21_6> tagRewriter = new TagRewriter<>(this);\n    private final BackwardsRegistryRewriter registryDataRewriter = new BackwardsRegistryRewriter(this);\n    private final RecipeDisplayRewriter<ClientboundPacket1_21_6> recipeRewriter = new RecipeDisplayRewriter1_21_5<>(this);\n    private final BlockRewriter<ClientboundPacket1_21_6> blockRewriter = BlockRewriter.for1_20_2(this, ChunkType1_21_5::new);\n\n    public Protocol1_21_7To1_21_6() {\n        super(ClientboundPacket1_21_6.class, ClientboundPacket1_21_6.class, ServerboundPacket1_21_6.class, ServerboundPacket1_21_6.class);\n    }\n\n    @Override\n    public void init(final UserConnection connection) {\n        addEntityTracker(connection, new EntityTrackerBase(connection, EntityTypes1_21_6.PLAYER));\n        addItemHasher(connection, new ItemHasherBase(this, connection));\n    }\n\n    @Override\n    public BackwardsMappingData getMappingData() {\n        return MAPPINGS;\n    }\n\n    @Override\n    public Types1_20_5<StructuredDataKeys1_21_5, EntityDataTypes1_21_5> types() {\n        return VersionedTypes.V1_21_6;\n    }\n\n    @Override\n    public Types1_20_5<StructuredDataKeys1_21_5, EntityDataTypes1_21_5> mappedTypes() {\n        return VersionedTypes.V1_21_6;\n    }\n\n    @Override\n    public NBTComponentRewriter<ClientboundPacket1_21_6> getComponentRewriter() {\n        return translatableRewriter;\n    }\n\n    @Override\n    public EntityPacketRewriter1_21_7 getEntityRewriter() {\n        return entityRewriter;\n    }\n\n    @Override\n    public BlockItemPacketRewriter1_21_7 getItemRewriter() {\n        return itemRewriter;\n    }\n\n    @Override\n    public BlockRewriter<ClientboundPacket1_21_6> getBlockRewriter() {\n        return blockRewriter;\n    }\n\n    @Override\n    public BackwardsRegistryRewriter getRegistryDataRewriter() {\n        return registryDataRewriter;\n    }\n\n    @Override\n    public RecipeDisplayRewriter<ClientboundPacket1_21_6> getRecipeRewriter() {\n        return recipeRewriter;\n    }\n\n    @Override\n    public ParticleRewriter<ClientboundPacket1_21_6> getParticleRewriter() {\n        return particleRewriter;\n    }\n\n    @Override\n    public TagRewriter<ClientboundPacket1_21_6> getTagRewriter() {\n        return tagRewriter;\n    }\n\n    @Override\n    protected PacketTypesProvider<ClientboundPacket1_21_6, ClientboundPacket1_21_6, ServerboundPacket1_21_6, ServerboundPacket1_21_6> createPacketTypesProvider() {\n        return new SimplePacketTypesProvider<>(\n            packetTypeMap(unmappedClientboundPacketType, ClientboundPackets1_21_6.class, ClientboundConfigurationPackets1_21_6.class),\n            packetTypeMap(mappedClientboundPacketType, ClientboundPackets1_21_6.class, ClientboundConfigurationPackets1_21_6.class),\n            packetTypeMap(mappedServerboundPacketType, ServerboundPackets1_21_6.class, ServerboundConfigurationPackets1_21_6.class),\n            packetTypeMap(unmappedServerboundPacketType, ServerboundPackets1_21_6.class, ServerboundConfigurationPackets1_21_6.class)\n        );\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_7to1_21_6/rewriter/BlockItemPacketRewriter1_21_7.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_7to1_21_6.rewriter;\n\nimport com.viaversion.viabackwards.api.rewriters.BackwardsStructuredItemRewriter;\nimport com.viaversion.viabackwards.protocol.v1_21_7to1_21_6.Protocol1_21_7To1_21_6;\nimport com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ClientboundPacket1_21_6;\nimport com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ServerboundPacket1_21_6;\n\npublic final class BlockItemPacketRewriter1_21_7 extends BackwardsStructuredItemRewriter<ClientboundPacket1_21_6, ServerboundPacket1_21_6, Protocol1_21_7To1_21_6> {\n\n    public BlockItemPacketRewriter1_21_7(final Protocol1_21_7To1_21_6 protocol) {\n        super(protocol);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_7to1_21_6/rewriter/EntityPacketRewriter1_21_7.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_7to1_21_6.rewriter;\n\nimport com.viaversion.viabackwards.api.rewriters.EntityRewriter;\nimport com.viaversion.viabackwards.protocol.v1_21_7to1_21_6.Protocol1_21_7To1_21_6;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityType;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_21_6;\nimport com.viaversion.viaversion.api.minecraft.entitydata.types.EntityDataTypes1_21_5;\nimport com.viaversion.viaversion.api.type.types.version.VersionedTypes;\nimport com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ClientboundPacket1_21_6;\nimport com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ClientboundPackets1_21_6;\n\npublic final class EntityPacketRewriter1_21_7 extends EntityRewriter<ClientboundPacket1_21_6, Protocol1_21_7To1_21_6> {\n\n    public EntityPacketRewriter1_21_7(final Protocol1_21_7To1_21_6 protocol) {\n        super(protocol, VersionedTypes.V1_21_6.entityDataTypes.optionalComponentType, VersionedTypes.V1_21_6.entityDataTypes.booleanType);\n    }\n\n    @Override\n    protected void registerRewrites() {\n        final EntityDataTypes1_21_5 mappedEntityDataTypes = VersionedTypes.V1_21_6.entityDataTypes;\n        registerEntityDataTypeHandler1_20_3(\n            mappedEntityDataTypes.itemType,\n            mappedEntityDataTypes.blockStateType,\n            mappedEntityDataTypes.optionalBlockStateType,\n            mappedEntityDataTypes.particleType,\n            mappedEntityDataTypes.particlesType,\n            mappedEntityDataTypes.componentType,\n            mappedEntityDataTypes.optionalComponentType\n        );\n    }\n\n    @Override\n    public EntityType typeFromId(final int type) {\n        return EntityTypes1_21_6.getTypeFromId(type);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_9to1_21_7/Protocol1_21_9To1_21_7.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_9to1_21_7;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.viabackwards.ViaBackwards;\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viabackwards.api.data.BackwardsMappingData;\nimport com.viaversion.viabackwards.api.rewriters.BackwardsRegistryRewriter;\nimport com.viaversion.viabackwards.api.rewriters.SoundRewriter;\nimport com.viaversion.viabackwards.api.rewriters.text.NBTComponentRewriter;\nimport com.viaversion.viabackwards.protocol.v1_21_9to1_21_7.rewriter.BlockItemPacketRewriter1_21_9;\nimport com.viaversion.viabackwards.protocol.v1_21_9to1_21_7.rewriter.ComponentRewriter1_21_9;\nimport com.viaversion.viabackwards.protocol.v1_21_9to1_21_7.rewriter.EntityPacketRewriter1_21_9;\nimport com.viaversion.viabackwards.protocol.v1_21_9to1_21_7.rewriter.ParticleRewriter1_21_9;\nimport com.viaversion.viabackwards.protocol.v1_21_9to1_21_7.rewriter.RegistryDataRewriter1_21_9;\nimport com.viaversion.viabackwards.protocol.v1_21_9to1_21_7.storage.DimensionScaleStorage;\nimport com.viaversion.viabackwards.protocol.v1_21_9to1_21_7.storage.PlayerRotationStorage;\nimport com.viaversion.viabackwards.protocol.v1_21_9to1_21_7.tracker.EntityTracker1_21_9;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.data.version.StructuredDataKeys1_21_5;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_21_9;\nimport com.viaversion.viaversion.api.minecraft.entitydata.types.EntityDataTypes1_21_5;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.protocol.packet.provider.PacketTypesProvider;\nimport com.viaversion.viaversion.api.protocol.packet.provider.SimplePacketTypesProvider;\nimport com.viaversion.viaversion.api.protocol.version.ProtocolVersion;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.chunk.ChunkType1_21_5;\nimport com.viaversion.viaversion.api.type.types.version.Types1_20_5;\nimport com.viaversion.viaversion.api.type.types.version.VersionedTypes;\nimport com.viaversion.viaversion.api.type.types.version.VersionedTypesHolder;\nimport com.viaversion.viaversion.data.item.ItemHasherBase;\nimport com.viaversion.viaversion.protocols.v1_21_4to1_21_5.rewriter.RecipeDisplayRewriter1_21_5;\nimport com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ClientboundConfigurationPackets1_21_6;\nimport com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ClientboundPacket1_21_6;\nimport com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ClientboundPackets1_21_6;\nimport com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ServerboundConfigurationPackets1_21_6;\nimport com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ServerboundPacket1_21_6;\nimport com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ServerboundPackets1_21_6;\nimport com.viaversion.viaversion.protocols.v1_21_7to1_21_9.Protocol1_21_7To1_21_9;\nimport com.viaversion.viaversion.protocols.v1_21_7to1_21_9.packet.ClientboundConfigurationPackets1_21_9;\nimport com.viaversion.viaversion.protocols.v1_21_7to1_21_9.packet.ClientboundPacket1_21_9;\nimport com.viaversion.viaversion.protocols.v1_21_7to1_21_9.packet.ClientboundPackets1_21_9;\nimport com.viaversion.viaversion.protocols.v1_21_7to1_21_9.packet.ServerboundConfigurationPackets1_21_9;\nimport com.viaversion.viaversion.protocols.v1_21_7to1_21_9.packet.ServerboundPacket1_21_9;\nimport com.viaversion.viaversion.protocols.v1_21to1_21_2.storage.BundleStateTracker;\nimport com.viaversion.viaversion.rewriter.BlockRewriter;\nimport com.viaversion.viaversion.rewriter.ParticleRewriter;\nimport com.viaversion.viaversion.rewriter.RecipeDisplayRewriter;\nimport com.viaversion.viaversion.rewriter.TagRewriter;\nimport com.viaversion.viaversion.rewriter.block.BlockRewriter1_21_5;\n\nimport static com.viaversion.viaversion.util.ProtocolUtil.packetTypeMap;\n\npublic final class Protocol1_21_9To1_21_7 extends BackwardsProtocol<ClientboundPacket1_21_9, ClientboundPacket1_21_6, ServerboundPacket1_21_9, ServerboundPacket1_21_6> {\n\n    public static final BackwardsMappingData MAPPINGS = new BackwardsMappingData(\"1.21.9\", \"1.21.7\", Protocol1_21_7To1_21_9.class);\n    private final EntityPacketRewriter1_21_9 entityRewriter = new EntityPacketRewriter1_21_9(this);\n    private final BlockItemPacketRewriter1_21_9 itemRewriter = new BlockItemPacketRewriter1_21_9(this);\n    private final ParticleRewriter<ClientboundPacket1_21_9> particleRewriter = new ParticleRewriter1_21_9(this);\n    private final NBTComponentRewriter<ClientboundPacket1_21_9> translatableRewriter = new ComponentRewriter1_21_9(this);\n    private final TagRewriter<ClientboundPacket1_21_9> tagRewriter = new TagRewriter<>(this);\n    private final RecipeDisplayRewriter<ClientboundPacket1_21_9> recipeRewriter = new RecipeDisplayRewriter1_21_5<>(this);\n    private final BackwardsRegistryRewriter registryDataRewriter = new RegistryDataRewriter1_21_9(this);\n    private final BlockRewriter<ClientboundPacket1_21_9> blockRewriter = new BlockRewriter1_21_5<>(this, ChunkType1_21_5::new);\n\n    public Protocol1_21_9To1_21_7() {\n        super(ClientboundPacket1_21_9.class, ClientboundPacket1_21_6.class, ServerboundPacket1_21_9.class, ServerboundPacket1_21_6.class);\n    }\n\n    @Override\n    protected void registerPackets() {\n        super.registerPackets();\n\n        final SoundRewriter<ClientboundPacket1_21_9> soundRewriter = new SoundRewriter<>(this);\n        replaceClientbound(ClientboundPackets1_21_9.EXPLODE, wrapper -> {\n            wrapper.passthrough(Types.DOUBLE); // X\n            wrapper.passthrough(Types.DOUBLE); // Y\n            wrapper.passthrough(Types.DOUBLE); // Z\n\n            wrapper.read(Types.FLOAT); // Radius\n            wrapper.read(Types.INT); // Affected blocks\n\n            if (wrapper.passthrough(Types.BOOLEAN)) {\n                wrapper.passthrough(Types.DOUBLE); // Knockback X\n                wrapper.passthrough(Types.DOUBLE); // Knockback Y\n                wrapper.passthrough(Types.DOUBLE); // Knockback Z\n            }\n\n            particleRewriter.passthroughParticle(wrapper); // Explosion particle\n            soundRewriter.soundHolderHandler().handle(wrapper);\n\n            final int blockParticles = wrapper.read(Types.VAR_INT);\n            for (int i = 0; i < blockParticles; i++) {\n                wrapper.read(particleRewriter.particleType());\n                wrapper.read(Types.FLOAT); // Scaling\n                wrapper.read(Types.FLOAT); // Speed\n                wrapper.read(Types.VAR_INT); // Weight\n            }\n        });\n\n        registerClientbound(ClientboundConfigurationPackets1_21_9.CODE_OF_CONDUCT, ClientboundConfigurationPackets1_21_6.SHOW_DIALOG, wrapper -> {\n            // TODO Remove once we implement dialogs during the configuration state in 1.21.6->1.21.5\n            final boolean supportsDialogs = wrapper.user().getProtocolInfo().protocolVersion().newerThan(ProtocolVersion.v1_21_5);\n            if (!ViaBackwards.getConfig().codeOfConductAsDialog() || !supportsDialogs) {\n                wrapper.cancel();\n\n                final PacketWrapper acceptPacket = wrapper.create(ServerboundConfigurationPackets1_21_9.ACCEPT_CODE_OF_CONDUCT);\n                acceptPacket.sendToServer(Protocol1_21_9To1_21_7.class);\n                return;\n            }\n\n            final String codeOfConduct = wrapper.read(Types.STRING);\n\n            final CompoundTag tag = new CompoundTag();\n            tag.putString(\"type\", \"minecraft:confirmation\");\n            tag.putString(\"title\", translatableRewriter.mappedTranslationKey(\"multiplayer.codeOfConduct.title\"));\n\n            final CompoundTag body = new CompoundTag();\n            body.putString(\"type\", \"minecraft:plain_message\");\n            body.putString(\"contents\", codeOfConduct);\n            tag.put(\"body\", body);\n\n            final CompoundTag yes = new CompoundTag();\n            final CompoundTag yesLabel = new CompoundTag();\n            yesLabel.putString(\"translate\", \"gui.acknowledge\");\n            yes.put(\"label\", yesLabel);\n\n            final CompoundTag acceptAction = new CompoundTag();\n            acceptAction.putString(\"type\", \"minecraft:custom\");\n            acceptAction.putString(\"id\", \"viabackwards:ack_code_of_conduct\");\n            yes.put(\"action\", acceptAction);\n            tag.put(\"yes\", yes);\n\n            final CompoundTag no = new CompoundTag();\n            final CompoundTag noLabel = new CompoundTag();\n            noLabel.putString(\"translate\", \"menu.disconnect\");\n            no.put(\"label\", noLabel);\n\n            final CompoundTag disconnectAction = new CompoundTag();\n            disconnectAction.putString(\"type\", \"minecraft:custom\");\n            disconnectAction.putString(\"id\", \"viabackwards:disconnect\");\n            no.put(\"action\", disconnectAction);\n            tag.put(\"no\", no);\n\n            wrapper.write(Types.TRUSTED_TAG, tag);\n        });\n\n        registerServerbound(ServerboundConfigurationPackets1_21_6.CUSTOM_CLICK_ACTION, wrapper -> {\n            final String id = wrapper.passthrough(Types.STRING);\n            if (\"viabackwards:ack_code_of_conduct\".equals(id)) {\n                wrapper.cancel();\n                final PacketWrapper acceptPacket = wrapper.create(ServerboundConfigurationPackets1_21_9.ACCEPT_CODE_OF_CONDUCT);\n                acceptPacket.sendToServer(Protocol1_21_9To1_21_7.class);\n            } else if (\"viabackwards:disconnect\".equals(id)) {\n                wrapper.cancel();\n                wrapper.user().disconnect(translatableRewriter.mappedTranslationKey(\"multiplayer.disconnect.code_of_conduct\"));\n            }\n        });\n\n        registerServerbound(ServerboundPackets1_21_6.DEBUG_SAMPLE_SUBSCRIPTION, wrapper -> {\n            final int sampleType = wrapper.read(Types.VAR_INT);\n            if (sampleType == 0) { // TICK_TIME\n                wrapper.write(Types.VAR_INT, 1); // Subscription count\n                wrapper.write(Types.VAR_INT, 0); // Subscription registry id (DEDICATED_SERVER_TICK_TIME)\n            }\n        });\n        registerClientbound(ClientboundPackets1_21_9.BUNDLE_DELIMITER, wrapper -> wrapper.user().get(BundleStateTracker.class).toggleBundling());\n\n        cancelClientbound(ClientboundPackets1_21_9.DEBUG_BLOCK_VALUE);\n        cancelClientbound(ClientboundPackets1_21_9.DEBUG_CHUNK_VALUE);\n        cancelClientbound(ClientboundPackets1_21_9.DEBUG_ENTITY_VALUE);\n        cancelClientbound(ClientboundPackets1_21_9.DEBUG_EVENT);\n        cancelClientbound(ClientboundPackets1_21_9.GAME_EVENT_TEST_HIGHLIGHT_POS);\n    }\n\n    @Override\n    public void init(final UserConnection connection) {\n        addEntityTracker(connection, new EntityTracker1_21_9(connection, EntityTypes1_21_9.PLAYER));\n        addItemHasher(connection, new ItemHasherBase(this, connection));\n        connection.put(new PlayerRotationStorage());\n        connection.put(new DimensionScaleStorage());\n        connection.put(new BundleStateTracker());\n    }\n\n    @Override\n    public BackwardsMappingData getMappingData() {\n        return MAPPINGS;\n    }\n\n    @Override\n    public EntityPacketRewriter1_21_9 getEntityRewriter() {\n        return entityRewriter;\n    }\n\n    @Override\n    public BlockItemPacketRewriter1_21_9 getItemRewriter() {\n        return itemRewriter;\n    }\n\n    @Override\n    public BlockRewriter<ClientboundPacket1_21_9> getBlockRewriter() {\n        return blockRewriter;\n    }\n\n    @Override\n    public BackwardsRegistryRewriter getRegistryDataRewriter() {\n        return registryDataRewriter;\n    }\n\n    @Override\n    public ParticleRewriter<ClientboundPacket1_21_9> getParticleRewriter() {\n        return particleRewriter;\n    }\n\n    @Override\n    public NBTComponentRewriter<ClientboundPacket1_21_9> getComponentRewriter() {\n        return translatableRewriter;\n    }\n\n    @Override\n    public TagRewriter<ClientboundPacket1_21_9> getTagRewriter() {\n        return tagRewriter;\n    }\n\n    @Override\n    public RecipeDisplayRewriter<ClientboundPacket1_21_9> getRecipeRewriter() {\n        return recipeRewriter;\n    }\n\n    @Override\n    public VersionedTypesHolder types() {\n        return VersionedTypes.V1_21_9;\n    }\n\n    @Override\n    public Types1_20_5<StructuredDataKeys1_21_5, EntityDataTypes1_21_5> mappedTypes() {\n        return VersionedTypes.V1_21_6;\n    }\n\n    @Override\n    protected PacketTypesProvider<ClientboundPacket1_21_9, ClientboundPacket1_21_6, ServerboundPacket1_21_9, ServerboundPacket1_21_6> createPacketTypesProvider() {\n        return new SimplePacketTypesProvider<>(\n            packetTypeMap(unmappedClientboundPacketType, ClientboundPackets1_21_9.class, ClientboundConfigurationPackets1_21_9.class),\n            packetTypeMap(mappedClientboundPacketType, ClientboundPackets1_21_6.class, ClientboundConfigurationPackets1_21_6.class),\n            packetTypeMap(mappedServerboundPacketType, ServerboundPackets1_21_6.class, ServerboundConfigurationPackets1_21_9.class),\n            packetTypeMap(unmappedServerboundPacketType, ServerboundPackets1_21_6.class, ServerboundConfigurationPackets1_21_6.class)\n        );\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_9to1_21_7/rewriter/BlockItemPacketRewriter1_21_9.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_9to1_21_7.rewriter;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.viabackwards.api.rewriters.BackwardsStructuredItemRewriter;\nimport com.viaversion.viabackwards.protocol.v1_21_9to1_21_7.Protocol1_21_9To1_21_7;\nimport com.viaversion.viabackwards.protocol.v1_21_9to1_21_7.storage.DimensionScaleStorage;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.data.entity.EntityTracker;\nimport com.viaversion.viaversion.api.minecraft.ResolvableProfile;\nimport com.viaversion.viaversion.api.minecraft.data.StructuredDataContainer;\nimport com.viaversion.viaversion.api.minecraft.data.StructuredDataKey;\nimport com.viaversion.viaversion.api.minecraft.item.Item;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ServerboundPacket1_21_6;\nimport com.viaversion.viaversion.protocols.v1_21_7to1_21_9.packet.ClientboundPacket1_21_9;\nimport com.viaversion.viaversion.protocols.v1_21_7to1_21_9.packet.ClientboundPackets1_21_9;\n\nimport static com.viaversion.viaversion.protocols.v1_21_7to1_21_9.rewriter.BlockItemPacketRewriter1_21_9.downgradeData;\nimport static com.viaversion.viaversion.protocols.v1_21_7to1_21_9.rewriter.BlockItemPacketRewriter1_21_9.upgradeData;\n\npublic final class BlockItemPacketRewriter1_21_9 extends BackwardsStructuredItemRewriter<ClientboundPacket1_21_9, ServerboundPacket1_21_6, Protocol1_21_9To1_21_7> {\n\n    public BlockItemPacketRewriter1_21_9(final Protocol1_21_9To1_21_7 protocol) {\n        super(protocol);\n    }\n\n    @Override\n    public void registerPackets() {\n        protocol.registerClientbound(ClientboundPackets1_21_9.INITIALIZE_BORDER, this::updateBorderCenter);\n        protocol.registerClientbound(ClientboundPackets1_21_9.SET_BORDER_CENTER, this::updateBorderCenter);\n    }\n\n    @Override\n    protected void handleItemDataComponentsToClient(final UserConnection connection, final Item item, final StructuredDataContainer container) {\n        super.handleItemDataComponentsToClient(connection, item, container);\n        downgradeData(item, container);\n    }\n\n    @Override\n    protected void handleItemDataComponentsToServer(final UserConnection connection, final Item item, final StructuredDataContainer container) {\n        super.handleItemDataComponentsToServer(connection, item, container);\n        upgradeData(item, container);\n    }\n\n    @Override\n    protected void backupInconvertibleData(final UserConnection connection, final Item item, final StructuredDataContainer dataContainer, final CompoundTag backupTag) {\n        super.backupInconvertibleData(connection, item, dataContainer, backupTag);\n        final ResolvableProfile profile = dataContainer.get(StructuredDataKey.PROFILE1_21_9);\n        if (profile != null) {\n            final CompoundTag profileTag = new CompoundTag();\n            if (profile.bodyTexture() != null) {\n                profileTag.putString(\"body_texture\", profile.bodyTexture());\n            }\n            if (profile.capeTexture() != null) {\n                profileTag.putString(\"cape_texture\", profile.capeTexture());\n            }\n            if (profile.elytraTexture() != null) {\n                profileTag.putString(\"elytra_texture\", profile.elytraTexture());\n            }\n            if (profile.modelType() != null) {\n                profileTag.putBoolean(\"model\", profile.modelType() == 0);\n            }\n            if (!profileTag.isEmpty()) {\n                backupTag.put(\"profile\", profileTag);\n            }\n        }\n    }\n\n    @Override\n    protected void restoreBackupData(final Item item, final StructuredDataContainer container, final CompoundTag customData) {\n        super.restoreBackupData(item, container, customData);\n        if (!(customData.remove(nbtTagName(\"backup\")) instanceof final CompoundTag backupTag)) {\n            return;\n        }\n        final CompoundTag profileTag = backupTag.getCompoundTag(\"profile\");\n        if (profileTag != null) {\n            container.replace(StructuredDataKey.PROFILE1_20_5, StructuredDataKey.PROFILE1_21_9, profile -> new ResolvableProfile(\n                profile,\n                profileTag.getString(\"body_texture\"),\n                profileTag.getString(\"cape_texture\"),\n                profileTag.getString(\"elytra_texture\"),\n                profileTag.contains(\"model\") ? (profileTag.getBoolean(\"model\") ? 0 : 1) : null\n            ));\n        }\n    }\n\n    private void updateBorderCenter(final PacketWrapper wrapper) {\n        double centerX = wrapper.read(Types.DOUBLE);\n        double centerZ = wrapper.read(Types.DOUBLE);\n\n        final EntityTracker tracker = protocol.getEntityRewriter().tracker(wrapper.user());\n        if (tracker.currentDimensionId() != -1) {\n            final double scale = wrapper.user().get(DimensionScaleStorage.class).getScale(tracker.currentDimensionId());\n            centerX *= scale;\n            centerZ *= scale;\n        }\n\n        wrapper.write(Types.DOUBLE, centerX);\n        wrapper.write(Types.DOUBLE, centerZ);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_9to1_21_7/rewriter/ComponentRewriter1_21_9.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_9to1_21_7.rewriter;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.StringTag;\nimport com.viaversion.nbt.tag.Tag;\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viabackwards.api.rewriters.text.NBTComponentRewriter;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.data.StructuredDataKey;\nimport com.viaversion.viaversion.protocols.v1_21_7to1_21_9.packet.ClientboundPacket1_21_9;\n\npublic final class ComponentRewriter1_21_9 extends NBTComponentRewriter<ClientboundPacket1_21_9> {\n\n    public ComponentRewriter1_21_9(final BackwardsProtocol<ClientboundPacket1_21_9, ?, ?, ?> protocol) {\n        super(protocol);\n    }\n\n    @Override\n    protected void processCompoundTag(final UserConnection connection, final CompoundTag tag) {\n        super.processCompoundTag(connection, tag);\n\n        // Throw out the new object type and its properties\n        final String type = tag.getString(\"type\");\n\n        // Try to use the 26.1+ fallback value if present, otherwise just show an empty string\n        Tag fallback = tag.get(\"fallback\");\n        if (fallback == null) {\n            fallback = new StringTag(\"\");\n        }\n\n        if (\"object\".equals(type)) {\n            tag.put(\"text\", fallback);\n            tag.remove(\"type\");\n        }\n        if (tag.remove(\"sprite\") != null) {\n            tag.put(\"text\", fallback);\n        }\n        if (tag.remove(\"player\") != null) {\n            tag.put(\"text\", fallback);\n        }\n        tag.remove(\"atlas\");\n    }\n\n    @Override\n    protected void handleShowItem(final UserConnection connection, final CompoundTag itemTag, final CompoundTag componentsTag) {\n        super.handleShowItem(connection, itemTag, componentsTag);\n        if (componentsTag == null) {\n            return;\n        }\n\n        removeDataComponents(componentsTag, StructuredDataKey.ENTITY_DATA1_21_9, StructuredDataKey.BLOCK_ENTITY_DATA1_21_9);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_9to1_21_7/rewriter/EntityPacketRewriter1_21_9.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_9to1_21_7.rewriter;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.StringTag;\nimport com.viaversion.nbt.tag.Tag;\nimport com.viaversion.viabackwards.api.rewriters.EntityRewriter;\nimport com.viaversion.viabackwards.protocol.v1_21_9to1_21_7.Protocol1_21_9To1_21_7;\nimport com.viaversion.viabackwards.protocol.v1_21_9to1_21_7.storage.MannequinData;\nimport com.viaversion.viabackwards.protocol.v1_21_9to1_21_7.storage.PlayerRotationStorage;\nimport com.viaversion.viabackwards.protocol.v1_21_9to1_21_7.tracker.EntityTracker1_21_9;\nimport com.viaversion.viabackwards.utils.VelocityUtil;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.data.entity.TrackedEntity;\nimport com.viaversion.viaversion.api.minecraft.BlockPosition;\nimport com.viaversion.viaversion.api.minecraft.GameProfile;\nimport com.viaversion.viaversion.api.minecraft.GlobalBlockPosition;\nimport com.viaversion.viaversion.api.minecraft.ResolvableProfile;\nimport com.viaversion.viaversion.api.minecraft.Vector3d;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityType;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_21_6;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_21_9;\nimport com.viaversion.viaversion.api.minecraft.entitydata.EntityData;\nimport com.viaversion.viaversion.api.minecraft.entitydata.types.EntityDataTypes1_21_5;\nimport com.viaversion.viaversion.api.minecraft.item.Item;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.version.VersionedTypes;\nimport com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ClientboundPackets1_21_6;\nimport com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ServerboundPackets1_21_6;\nimport com.viaversion.viaversion.protocols.v1_21_7to1_21_9.packet.ClientboundPacket1_21_9;\nimport com.viaversion.viaversion.protocols.v1_21_7to1_21_9.packet.ClientboundPackets1_21_9;\nimport com.viaversion.viaversion.protocols.v1_21to1_21_2.storage.BundleStateTracker;\nimport com.viaversion.viaversion.rewriter.entitydata.EntityDataHandler;\nimport com.viaversion.viaversion.util.ChatColorUtil;\nimport com.viaversion.viaversion.util.Copyable;\nimport java.util.BitSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.UUID;\nimport java.util.concurrent.ThreadLocalRandom;\nimport org.checkerframework.checker.nullness.qual.Nullable;\n\npublic final class EntityPacketRewriter1_21_9 extends EntityRewriter<ClientboundPacket1_21_9, Protocol1_21_9To1_21_7> {\n\n    public EntityPacketRewriter1_21_9(final Protocol1_21_9To1_21_7 protocol) {\n        super(protocol, VersionedTypes.V1_21_9.entityDataTypes.optionalComponentType, VersionedTypes.V1_21_9.entityDataTypes.booleanType);\n    }\n\n    @Override\n    public void registerPackets() {\n        protocol.replaceClientbound(ClientboundPackets1_21_9.ADD_ENTITY, wrapper -> {\n            final int entityId = wrapper.passthrough(Types.VAR_INT);\n            final UUID uuid = wrapper.passthrough(Types.UUID);\n            final int entityTypeId = wrapper.passthrough(Types.VAR_INT);\n\n            final double x = wrapper.passthrough(Types.DOUBLE);\n            final double y = wrapper.passthrough(Types.DOUBLE);\n            final double z = wrapper.passthrough(Types.DOUBLE);\n\n            final Vector3d movement = wrapper.read(Types.LOW_PRECISION_VECTOR);\n\n            final byte pitch = wrapper.passthrough(Types.BYTE);\n            final byte yaw = wrapper.passthrough(Types.BYTE);\n            final byte headYaw = wrapper.passthrough(Types.BYTE);\n\n            final int data = wrapper.passthrough(Types.VAR_INT);\n            final EntityType entityType = trackAndRewrite(wrapper, entityTypeId, entityId);\n            if (protocol.getMappingData() != null && entityType == EntityTypes1_21_9.FALLING_BLOCK) {\n                final int mappedBlockStateId = protocol.getMappingData().getNewBlockStateId(data);\n                wrapper.set(Types.VAR_INT, 2, mappedBlockStateId);\n            }\n\n            writeMovementShorts(wrapper, movement);\n\n            if (EntityTypes1_21_9.getTypeFromId(entityTypeId) == EntityTypes1_21_9.MANNEQUIN) {\n                final String name = randomHackyEmptyName();\n                final MannequinData mannequinData = new MannequinData(uuid, name);\n                final TrackedEntity trackedEntity = tracker(wrapper.user()).entity(entityId);\n\n                trackedEntity.data().put(mannequinData);\n                sendInitialPlayerInfoUpdate(wrapper.user(), mannequinData, new GameProfile.Property[0]);\n\n                mannequinData.setPosition(x, y, z);\n                mannequinData.setRotation(yaw, pitch);\n                mannequinData.setHeadYaw(headYaw);\n            }\n        });\n\n        // Track movement\n        protocol.registerClientbound(ClientboundPackets1_21_9.TELEPORT_ENTITY, this::trackMannequinTeleport);\n        protocol.registerClientbound(ClientboundPackets1_21_9.ENTITY_POSITION_SYNC, this::trackMannequinTeleport);\n        protocol.registerClientbound(ClientboundPackets1_21_9.MOVE_ENTITY_POS, wrapper -> storeMovementMannequinData(wrapper, true, false));\n        protocol.registerClientbound(ClientboundPackets1_21_9.MOVE_ENTITY_POS_ROT, wrapper -> storeMovementMannequinData(wrapper, true, true));\n        protocol.registerClientbound(ClientboundPackets1_21_9.MOVE_ENTITY_ROT, wrapper -> storeMovementMannequinData(wrapper, false, true));\n        protocol.registerClientbound(ClientboundPackets1_21_9.ROTATE_HEAD, wrapper -> {\n            final int vehicleId = wrapper.passthrough(Types.VAR_INT);\n            final byte headRotation = wrapper.passthrough(Types.BYTE);\n\n            final EntityTracker1_21_9 tracker = tracker(wrapper.user());\n            final TrackedEntity trackedEntity = tracker.entity(vehicleId);\n            if (trackedEntity != null && trackedEntity.hasData()) {\n                final MannequinData data = trackedEntity.data().get(MannequinData.class);\n                if (data != null) {\n                    data.setHeadYaw(headRotation);\n                }\n            }\n        });\n\n        // Track passengers\n        protocol.registerClientbound(ClientboundPackets1_21_9.SET_PASSENGERS, wrapper -> {\n            final int vehicleId = wrapper.passthrough(Types.VAR_INT);\n            final int[] passengerIds = wrapper.passthrough(Types.VAR_INT_ARRAY_PRIMITIVE);\n\n            final EntityTracker1_21_9 tracker = tracker(wrapper.user());\n            final TrackedEntity trackedEntity = tracker.entity(vehicleId);\n            if (trackedEntity != null && trackedEntity.hasData()) {\n                final MannequinData data = trackedEntity.data().get(MannequinData.class);\n                if (data != null) {\n                    data.setPassengers(passengerIds);\n                }\n            }\n        });\n\n        // Track items\n        protocol.replaceClientbound(ClientboundPackets1_21_9.SET_EQUIPMENT, wrapper -> {\n            final int entityId = wrapper.passthrough(Types.VAR_INT);\n\n            final TrackedEntity trackedEntity = tracker(wrapper.user()).entity(entityId);\n            final MannequinData mannequinData = trackedEntity != null && trackedEntity.hasData() ? trackedEntity.data().get(MannequinData.class) : null;\n\n            byte slot;\n            do {\n                slot = wrapper.passthrough(Types.BYTE);\n\n                final Item item = protocol.getItemRewriter().handleItemToClient(wrapper.user(), wrapper.read(protocol.getItemRewriter().itemType()));\n                wrapper.write(protocol.getItemRewriter().mappedItemType(), item);\n                if (mannequinData != null) {\n                    mannequinData.setEquipment((byte) (slot & 0x7F), item);\n                }\n            } while (slot < 0);\n        });\n\n        // Back to more non-mannequin things\n        protocol.registerClientbound(ClientboundPackets1_21_9.SET_ENTITY_MOTION, wrapper -> {\n            wrapper.passthrough(Types.VAR_INT); // Entity ID\n            writeMovementShorts(wrapper, wrapper.read(Types.LOW_PRECISION_VECTOR));\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_21_9.PLAYER_ROTATION, wrapper -> {\n            final PlayerRotationStorage storage = wrapper.user().get(PlayerRotationStorage.class);\n\n            float yRot = wrapper.read(Types.FLOAT);\n            if (wrapper.read(Types.BOOLEAN)) {\n                yRot = storage.yaw() + yRot;\n            }\n\n            float xRot = wrapper.read(Types.FLOAT);\n            if (wrapper.read(Types.BOOLEAN)) {\n                xRot = storage.pitch() + xRot;\n            }\n\n            wrapper.write(Types.FLOAT, yRot);\n            wrapper.write(Types.FLOAT, xRot);\n            storage.setRotation(yRot, xRot); // Update after having used its previous data\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_21_9.SET_DEFAULT_SPAWN_POSITION, wrapper -> {\n            final GlobalBlockPosition pos = wrapper.read(Types.GLOBAL_POSITION);\n            wrapper.write(Types.BLOCK_POSITION1_14, new BlockPosition(pos.x(), pos.y(), pos.z()));\n            wrapper.passthrough(Types.FLOAT); // Yaw\n            wrapper.read(Types.FLOAT); // Pitch\n        });\n\n        protocol.registerServerbound(ServerboundPackets1_21_6.MOVE_PLAYER_POS_ROT, wrapper -> {\n            wrapper.passthrough(Types.DOUBLE); // X\n            wrapper.passthrough(Types.DOUBLE); // Y\n            wrapper.passthrough(Types.DOUBLE); // Z\n\n            storePlayerRotation(wrapper);\n        });\n\n        protocol.registerServerbound(ServerboundPackets1_21_6.MOVE_PLAYER_ROT, this::storePlayerRotation);\n    }\n\n    private void trackMannequinTeleport(final PacketWrapper wrapper) {\n        final int entityId = wrapper.passthrough(Types.VAR_INT);\n        final EntityTracker1_21_9 tracker = tracker(wrapper.user());\n        final TrackedEntity trackedEntity = tracker.entity(entityId);\n        if (trackedEntity == null || !trackedEntity.hasData()) {\n            return;\n        }\n\n        final MannequinData mannequinData = trackedEntity.data().get(MannequinData.class);\n        if (mannequinData == null) {\n            return;\n        }\n\n        final double x = wrapper.passthrough(Types.DOUBLE);\n        final double y = wrapper.passthrough(Types.DOUBLE);\n        final double z = wrapper.passthrough(Types.DOUBLE);\n\n        wrapper.passthrough(Types.DOUBLE); // Delta movement X\n        wrapper.passthrough(Types.DOUBLE); // Delta movement Y\n        wrapper.passthrough(Types.DOUBLE); // Delta movement Z\n\n        final byte yaw = (byte) Math.floor(wrapper.passthrough(Types.FLOAT) * 256F / 360F);\n        final byte pitch = (byte) Math.floor(wrapper.passthrough(Types.FLOAT) * 256F / 360F);\n\n        mannequinData.setPosition(x, y, z);\n        mannequinData.setRotation(yaw, pitch);\n    }\n\n    private void storeMovementMannequinData(final PacketWrapper wrapper, final boolean position, final boolean rotation) {\n        final int entityId = wrapper.passthrough(Types.VAR_INT);\n\n        final TrackedEntity trackedEntity = tracker(wrapper.user()).entity(entityId);\n        if (trackedEntity == null || !trackedEntity.hasData()) {\n            return;\n        }\n\n        final MannequinData mannequinData = trackedEntity.data().get(MannequinData.class);\n        if (mannequinData == null) {\n            return;\n        }\n\n        if (position) {\n            final double deltaX = wrapper.passthrough(Types.SHORT) / 4096.0;\n            final double deltaY = wrapper.passthrough(Types.SHORT) / 4096.0;\n            final double deltaZ = wrapper.passthrough(Types.SHORT) / 4096.0;\n            mannequinData.setPosition(mannequinData.x() + deltaX, mannequinData.y() + deltaY, mannequinData.z() + deltaZ);\n        }\n\n        if (rotation) {\n            final byte yaw = wrapper.passthrough(Types.BYTE);\n            final byte pitch = wrapper.passthrough(Types.BYTE);\n            mannequinData.setRotation(yaw, pitch);\n        }\n    }\n\n    private void sendInitialPlayerInfoUpdate(final UserConnection connection, final MannequinData mannequinData, final GameProfile.Property[] properties) {\n        final PacketWrapper playerInfo = PacketWrapper.create(ClientboundPackets1_21_6.PLAYER_INFO_UPDATE, connection);\n\n        final BitSet actions = new BitSet(8);\n        for (int i = 0; i < 8; i++) {\n            actions.set(i);\n        }\n        playerInfo.write(Types.PROFILE_ACTIONS_ENUM1_21_4, actions);\n        playerInfo.write(Types.VAR_INT, 1); // One entry\n        playerInfo.write(Types.UUID, mannequinData.uuid());\n        playerInfo.write(Types.STRING, mannequinData.name());\n        playerInfo.write(Types.PROFILE_PROPERTY_ARRAY, properties);\n        playerInfo.write(Types.BOOLEAN, false); // Session info\n        playerInfo.write(Types.VAR_INT, 0); // Gamemode\n        playerInfo.write(Types.BOOLEAN, false); // Listed\n        playerInfo.write(Types.VAR_INT, 0); // Latency\n        playerInfo.write(Types.TRUSTED_OPTIONAL_TAG, null);\n        playerInfo.write(Types.VAR_INT, 1000); // List order\n        playerInfo.write(Types.BOOLEAN, true); // Show hat\n        playerInfo.send(Protocol1_21_9To1_21_7.class);\n\n        sendPlayerTeamDisplayName(connection, mannequinData, mannequinData.displayName());\n    }\n\n    private void sendPlayerInfoDisplayNameUpdate(final UserConnection connection, final MannequinData mannequinData, @Nullable final Tag displayName) {\n        final PacketWrapper playerInfo = PacketWrapper.create(ClientboundPackets1_21_6.PLAYER_INFO_UPDATE, connection);\n        final BitSet actions = new BitSet(8);\n        actions.set(5);\n        playerInfo.write(Types.PROFILE_ACTIONS_ENUM1_21_4, actions);\n        playerInfo.write(Types.VAR_INT, 1);\n        playerInfo.write(Types.UUID, mannequinData.uuid());\n        playerInfo.write(Types.TRUSTED_OPTIONAL_TAG, displayName);\n        playerInfo.send(Protocol1_21_9To1_21_7.class);\n\n        sendPlayerTeamDisplayName(connection, mannequinData, displayName);\n    }\n\n    private void sendPlayerTeamDisplayName(final UserConnection connection, final MannequinData mannequinData, final Tag displayName) {\n        // Send the display name as a team prefix\n        final Tag nonNullDisplayName = displayName != null ? displayName : new StringTag(\"Mannequin\");\n        final PacketWrapper addTeam = PacketWrapper.create(ClientboundPackets1_21_6.SET_PLAYER_TEAM, connection);\n        addTeam.write(Types.STRING, mannequinData.name());\n        addTeam.write(Types.BYTE, mannequinData.hasTeam() ? (byte) 2 : 0); // Mode\n        addTeam.write(Types.TRUSTED_TAG, nonNullDisplayName); // Display Name\n        addTeam.write(Types.BYTE, (byte) 0); // Flags\n        addTeam.write(Types.VAR_INT, 0); // Nametag visibility\n        addTeam.write(Types.VAR_INT, 0); // Collision rule\n        addTeam.write(Types.VAR_INT, 15); // Color\n        addTeam.write(Types.TRUSTED_TAG, nonNullDisplayName); // Prefix\n        addTeam.write(Types.TRUSTED_TAG, new StringTag(\"\")); // Suffix\n        if (!mannequinData.hasTeam()) {\n            addTeam.write(Types.STRING_ARRAY, new String[]{mannequinData.name()});\n            mannequinData.setHasTeam(true);\n        }\n        addTeam.send(Protocol1_21_9To1_21_7.class);\n    }\n\n    private String randomHackyEmptyName() {\n        final StringBuilder builder = new StringBuilder();\n        // Player names cannot be updated after the initial add without fully respawning them;\n        // Stack random color codes to appear as an empty name, later filled with a team prefix\n        for (int i = 0; i < 8; i++) {\n            final int random = ThreadLocalRandom.current().nextInt(ChatColorUtil.ALL_CODES.length());\n            builder.append('§').append(ChatColorUtil.ALL_CODES.charAt(random));\n        }\n        return builder.toString();\n    }\n\n    private void writeMovementShorts(final PacketWrapper wrapper, final Vector3d movement) {\n        wrapper.write(Types.SHORT, VelocityUtil.toLegacyVelocity(movement.x()));\n        wrapper.write(Types.SHORT, VelocityUtil.toLegacyVelocity(movement.y()));\n        wrapper.write(Types.SHORT, VelocityUtil.toLegacyVelocity(movement.z()));\n    }\n\n    private void storePlayerRotation(final PacketWrapper wrapper) {\n        final float yaw = wrapper.passthrough(Types.FLOAT);\n        final float pitch = wrapper.passthrough(Types.FLOAT);\n\n        wrapper.user().get(PlayerRotationStorage.class).setRotation(yaw, pitch);\n    }\n\n    @Override\n    protected void registerRewrites() {\n        final EntityDataTypes1_21_5 entityDataTypes = protocol.mappedTypes().entityDataTypes();\n        dataTypeMapper()\n            .added(entityDataTypes.compoundTagType)\n            .removed(VersionedTypes.V1_21_9.entityDataTypes.copperGolemState)\n            .removed(VersionedTypes.V1_21_9.entityDataTypes.weatheringCopperState)\n            .removed(VersionedTypes.V1_21_9.entityDataTypes.mannequinProfileType)\n            .register();\n\n        registerEntityDataTypeHandler1_20_3(\n            entityDataTypes.itemType,\n            entityDataTypes.blockStateType,\n            entityDataTypes.optionalBlockStateType,\n            entityDataTypes.particleType,\n            entityDataTypes.particlesType,\n            entityDataTypes.componentType,\n            entityDataTypes.optionalComponentType\n        );\n\n        final EntityDataHandler shoulderDataHandler = (event, data) -> {\n            final CompoundTag entityTag = new CompoundTag();\n            final Integer value = data.value();\n            if (value != null) {\n                entityTag.putInt(\"id\", EntityTypes1_21_6.PARROT.getId());\n                entityTag.putInt(\"Variant\", value);\n            }\n            data.setTypeAndValue(protocol.mappedTypes().entityDataTypes.compoundTagType, entityTag);\n        };\n        filter().type(EntityTypes1_21_9.PLAYER).index(19).handler(shoulderDataHandler);\n        filter().type(EntityTypes1_21_9.PLAYER).index(20).handler(shoulderDataHandler);\n        filter().type(EntityTypes1_21_9.PLAYER).handler((event, data) -> {\n            if (event.index() == 15) {\n                event.setIndex(18);\n            } else if (event.index() == 16) {\n                event.setIndex(17);\n            } else if (event.index() == 17 || event.index() == 18) {\n                event.setIndex(event.index() - 2); // Move hearts and score back down\n            }\n        });\n\n        filter().type(EntityTypes1_21_9.MANNEQUIN).handler((event, data) -> {\n            if (event.index() == 2) { // Display name\n                final Tag displayName = data.value();\n                final MannequinData mannequinData = event.trackedEntity().data().get(MannequinData.class);\n                mannequinData.setDisplayName(displayName);\n                sendPlayerInfoDisplayNameUpdate(event.user(), mannequinData, displayName);\n            } else if (event.index() == 17) { // Profile\n                final boolean isBundling = event.user().get(BundleStateTracker.class).isBundling();\n                if (!isBundling) {\n                    final PacketWrapper bundleStart = PacketWrapper.create(ClientboundPackets1_21_6.BUNDLE_DELIMITER, event.user());\n                    bundleStart.send(Protocol1_21_9To1_21_7.class);\n                }\n\n                final ResolvableProfile profile = data.value();\n                final MannequinData mannequinData = event.trackedEntity().data().get(MannequinData.class);\n                final UUID uuid = mannequinData.uuid();\n\n                // Remove the old entity\n                final PacketWrapper removeEntityPacket = PacketWrapper.create(ClientboundPackets1_21_6.REMOVE_ENTITIES, event.user());\n                removeEntityPacket.write(Types.VAR_INT_ARRAY_PRIMITIVE, new int[]{event.entityId()});\n                removeEntityPacket.send(Protocol1_21_9To1_21_7.class);\n\n                final PacketWrapper playerInfoRemove = PacketWrapper.create(ClientboundPackets1_21_6.PLAYER_INFO_REMOVE, event.user());\n                playerInfoRemove.write(Types.UUID_ARRAY, new UUID[]{uuid});\n                playerInfoRemove.send(Protocol1_21_9To1_21_7.class);\n\n                // Spawn new entity\n                sendInitialPlayerInfoUpdate(event.user(), mannequinData, profile.profile().properties());\n\n                final PacketWrapper spawnEntityPacket = PacketWrapper.create(ClientboundPackets1_21_6.ADD_ENTITY, event.user());\n                spawnEntityPacket.write(Types.VAR_INT, event.entityId());\n                spawnEntityPacket.write(Types.UUID, mannequinData.uuid());\n                spawnEntityPacket.write(Types.VAR_INT, EntityTypes1_21_6.PLAYER.getId());\n                spawnEntityPacket.write(Types.DOUBLE, mannequinData.x());\n                spawnEntityPacket.write(Types.DOUBLE, mannequinData.y());\n                spawnEntityPacket.write(Types.DOUBLE, mannequinData.z());\n                spawnEntityPacket.write(Types.BYTE, mannequinData.pitch());\n                spawnEntityPacket.write(Types.BYTE, mannequinData.yaw());\n                spawnEntityPacket.write(Types.BYTE, mannequinData.headYaw());\n                spawnEntityPacket.write(Types.VAR_INT, 0); // Data\n                spawnEntityPacket.write(Types.SHORT, (short) 0); // Velocity X\n                spawnEntityPacket.write(Types.SHORT, (short) 0); // Velocity Y\n                spawnEntityPacket.write(Types.SHORT, (short) 0); // Velocity Z\n                spawnEntityPacket.send(Protocol1_21_9To1_21_7.class);\n\n                // Re-apply entity data previously set\n                final PacketWrapper setEntityDataPacket = PacketWrapper.create(ClientboundPackets1_21_6.SET_ENTITY_DATA, event.user());\n                setEntityDataPacket.write(Types.VAR_INT, event.entityId());\n                setEntityDataPacket.write(VersionedTypes.V1_21_6.entityDataList, mannequinData.entityData());\n                setEntityDataPacket.send(Protocol1_21_9To1_21_7.class);\n\n                // Re-attach all passengers\n                if (mannequinData.passengers() != null) {\n                    final PacketWrapper setPassengersPacket = PacketWrapper.create(ClientboundPackets1_21_6.SET_PASSENGERS, event.user());\n                    setPassengersPacket.write(Types.VAR_INT, event.entityId());\n                    setPassengersPacket.write(Types.VAR_INT_ARRAY_PRIMITIVE, mannequinData.passengers());\n                    setPassengersPacket.send(Protocol1_21_9To1_21_7.class);\n                }\n\n                // Put on items\n                if (!mannequinData.itemMap().isEmpty()) {\n                    final PacketWrapper equipment = PacketWrapper.create(ClientboundPackets1_21_6.SET_EQUIPMENT, event.user());\n                    equipment.write(Types.VAR_INT, event.entityId());\n                    int i = 0;\n                    for (final Map.Entry<Byte, Item> itemEntry : mannequinData.itemMap().entrySet()) {\n                        final boolean more = i < mannequinData.itemMap().size() - 1;\n                        equipment.write(Types.BYTE, more ? (byte) (itemEntry.getKey() | -128) : itemEntry.getKey());\n                        equipment.write(protocol.getItemRewriter().mappedItemType(), itemEntry.getValue());\n                        i++;\n                    }\n                    equipment.send(Protocol1_21_9To1_21_7.class);\n                }\n\n                final PacketWrapper setHeadRotation = PacketWrapper.create(ClientboundPackets1_21_6.ROTATE_HEAD, event.user());\n                setHeadRotation.write(Types.VAR_INT, event.entityId());\n                setHeadRotation.write(Types.BYTE, mannequinData.headYaw());\n                setHeadRotation.send(Protocol1_21_9To1_21_7.class);\n\n                if (!isBundling) {\n                    final PacketWrapper bundleStart = PacketWrapper.create(ClientboundPackets1_21_6.BUNDLE_DELIMITER, event.user());\n                    bundleStart.send(Protocol1_21_9To1_21_7.class);\n                }\n\n                event.cancel();\n            } else if (event.index() == 15) {\n                event.setIndex(18);\n            } else if (event.index() == 16) {\n                event.setIndex(17);\n            } else if (event.index() == 18) {\n                // TODO Immovable?\n                event.cancel();\n            } else if (event.index() == 19) {\n                event.cancel(); // Description\n            }\n        });\n    }\n\n    @Override\n    public void handleEntityData(final int entityId, final List<EntityData> dataList, final UserConnection connection) {\n        super.handleEntityData(entityId, dataList, connection);\n\n        final EntityTracker1_21_9 tracker = tracker(connection);\n        final EntityType entityType = tracker.entityType(entityId);\n        if (entityType == null || !entityType.isOrHasParent(EntityTypes1_21_9.MANNEQUIN)) {\n            return;\n        }\n\n        final MannequinData mannequinData = tracker.entity(entityId).data().get(MannequinData.class);\n        if (mannequinData == null) {\n            return;\n        }\n\n        final List<EntityData> entityData = mannequinData.entityData();\n        entityData.removeIf(first -> dataList.stream().anyMatch(second -> first.id() == second.id()));\n        for (final EntityData data : dataList) {\n            final Object value = data.value();\n            entityData.add(new EntityData(data.id(), data.dataType(), Copyable.copy(value)));\n        }\n    }\n\n    @Override\n    public void onMappingDataLoaded() {\n        super.onMappingDataLoaded();\n        mapEntityTypeWithData(EntityTypes1_21_9.COPPER_GOLEM, EntityTypes1_21_9.FROG).tagName();\n        mapEntityTypeWithData(EntityTypes1_21_9.MANNEQUIN, EntityTypes1_21_9.PLAYER);\n    }\n\n    @Override\n    public EntityType typeFromId(final int type) {\n        return EntityTypes1_21_9.getTypeFromId(type);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_9to1_21_7/rewriter/ParticleRewriter1_21_9.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_9to1_21_7.rewriter;\n\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.Particle;\nimport com.viaversion.viaversion.api.protocol.Protocol;\nimport com.viaversion.viaversion.protocols.v1_21_7to1_21_9.packet.ClientboundPacket1_21_9;\nimport com.viaversion.viaversion.rewriter.ParticleRewriter;\n\npublic final class ParticleRewriter1_21_9 extends ParticleRewriter<ClientboundPacket1_21_9> {\n\n    public ParticleRewriter1_21_9(final Protocol<ClientboundPacket1_21_9, ?, ?, ?> protocol) {\n        super(protocol);\n    }\n\n    @Override\n    public void rewriteParticle(final UserConnection connection, final Particle particle) {\n        super.rewriteParticle(connection, particle);\n\n        final String identifier = protocol.getMappingData().getParticleMappings().mappedIdentifier(particle.id());\n        if (\"minecraft:dragon_breath\".equals(identifier)) {\n            particle.removeArgument(0); // Power\n        } else if (\"minecraft:flash\".equals(identifier)) {\n            particle.removeArgument(0); // Color\n        } else if (\"minecraft:effect\".equals(identifier) || \"minecraft:instant_effect\".equals(identifier)) {\n            particle.removeArgument(1); // Power\n            particle.removeArgument(0); // Color\n        }\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_9to1_21_7/rewriter/RegistryDataRewriter1_21_9.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_9to1_21_7.rewriter;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viabackwards.api.rewriters.BackwardsRegistryRewriter;\nimport com.viaversion.viabackwards.protocol.v1_21_9to1_21_7.storage.DimensionScaleStorage;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.RegistryEntry;\n\npublic final class RegistryDataRewriter1_21_9 extends BackwardsRegistryRewriter {\n\n    public RegistryDataRewriter1_21_9(final BackwardsProtocol<?, ?, ?, ?> protocol) {\n        super(protocol);\n    }\n\n    @Override\n    public void trackDimensionAndBiomes(final UserConnection connection, final String registryKey, final RegistryEntry[] entries) {\n        super.trackDimensionAndBiomes(connection, registryKey, entries);\n        if (!registryKey.equals(\"dimension_type\")) {\n            return;\n        }\n\n        final DimensionScaleStorage dimensionScaleStorage = connection.get(DimensionScaleStorage.class);\n        for (int i = 0; i < entries.length; i++) {\n            final RegistryEntry entry = entries[i];\n            final CompoundTag dimension = (CompoundTag) entry.tag();\n            if (dimension == null) {\n                continue;\n            }\n\n            final double coordinateScale = dimension.getDouble(\"coordinate_scale\", 1);\n            dimensionScaleStorage.setScale(i, coordinateScale);\n        }\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_9to1_21_7/storage/DimensionScaleStorage.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_9to1_21_7.storage;\n\nimport com.viaversion.viaversion.api.connection.StorableObject;\nimport java.util.HashMap;\nimport java.util.Map;\n\npublic final class DimensionScaleStorage implements StorableObject {\n\n    private final Map<Integer, Double> dimensionScales = new HashMap<>(4);\n\n    public double getScale(final int dimensionId) {\n        return dimensionScales.getOrDefault(dimensionId, 1D);\n    }\n\n    public void setScale(final int dimensionId, final double scale) {\n        dimensionScales.put(dimensionId, scale);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_9to1_21_7/storage/MannequinData.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_9to1_21_7.storage;\n\nimport com.viaversion.nbt.tag.Tag;\nimport com.viaversion.viaversion.api.minecraft.entitydata.EntityData;\nimport com.viaversion.viaversion.api.minecraft.item.Item;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.UUID;\n\npublic final class MannequinData {\n    private final UUID uuid;\n    private final String name;\n    private boolean hasTeam;\n\n\n    private double x;\n    private double y;\n    private double z;\n\n    // Packed values\n    private byte yaw;\n    private byte pitch;\n    private byte headYaw;\n\n    private int[] passengers;\n\n    private final List<EntityData> entityData = new ArrayList<>();\n    private final Map<Byte, Item> itemMap = new HashMap<>();\n    private Tag displayName;\n\n    public MannequinData(final UUID uuid, final String name) {\n        this.uuid = uuid;\n        this.name = name;\n    }\n\n    public void setHasTeam(final boolean hasTeam) {\n        this.hasTeam = hasTeam;\n    }\n\n    public boolean hasTeam() {\n        return hasTeam;\n    }\n\n    public UUID uuid() {\n        return uuid;\n    }\n\n    public String name() {\n        return name;\n    }\n\n    public void setPosition(final double x, final double y, final double z) {\n        this.x = x;\n        this.y = y;\n        this.z = z;\n    }\n\n    public void setRotation(final byte yaw, final byte pitch) {\n        this.yaw = yaw;\n        this.pitch = pitch;\n    }\n\n    public void setPassengers(final int[] passengers) {\n        this.passengers = passengers;\n    }\n\n    public double x() {\n        return x;\n    }\n\n    public double y() {\n        return y;\n    }\n\n    public double z() {\n        return z;\n    }\n\n    public byte yaw() {\n        return yaw;\n    }\n\n    public byte pitch() {\n        return pitch;\n    }\n\n    public List<EntityData> entityData() {\n        return entityData;\n    }\n\n    public int[] passengers() {\n        return passengers;\n    }\n\n    public void setDisplayName(Tag displayName) {\n        this.displayName = displayName;\n    }\n\n    public Tag displayName() {\n        return displayName;\n    }\n\n    public void setEquipment(byte slot, Item item) {\n        itemMap.put(slot, item);\n    }\n\n    public Map<Byte, Item> itemMap() {\n        return itemMap;\n    }\n\n    public byte headYaw() {\n        return headYaw;\n    }\n\n    public void setHeadYaw(byte headYaw) {\n        this.headYaw = headYaw;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_9to1_21_7/storage/PlayerRotationStorage.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_9to1_21_7.storage;\n\nimport com.viaversion.viaversion.api.connection.StorableObject;\n\npublic final class PlayerRotationStorage implements StorableObject {\n\n    private float yaw, pitch;\n\n    public void setRotation(final float yaw, final float pitch) {\n        this.yaw = yaw;\n        this.pitch = pitch;\n    }\n\n    public float yaw() {\n        return yaw;\n    }\n\n    public float pitch() {\n        return pitch;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_9to1_21_7/tracker/EntityTracker1_21_9.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21_9to1_21_7.tracker;\n\nimport com.viaversion.viabackwards.protocol.v1_21_9to1_21_7.Protocol1_21_9To1_21_7;\nimport com.viaversion.viabackwards.protocol.v1_21_9to1_21_7.storage.MannequinData;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.data.entity.StoredEntityData;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityType;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.data.entity.EntityTrackerBase;\nimport com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ClientboundPackets1_21_6;\nimport java.util.UUID;\nimport org.checkerframework.checker.nullness.qual.Nullable;\n\npublic final class EntityTracker1_21_9 extends EntityTrackerBase {\n\n    public EntityTracker1_21_9(final UserConnection connection, @Nullable final EntityType playerType) {\n        super(connection, playerType);\n    }\n\n    @Override\n    public void removeEntity(final int id) {\n        sendRemovePacket(id);\n        super.removeEntity(id);\n    }\n\n    public void sendRemovePacket(final int id) {\n        final StoredEntityData entityData = entityDataIfPresent(id);\n        final MannequinData trackedEntityId;\n        if (entityData != null && (trackedEntityId = entityData.get(MannequinData.class)) != null) {\n            final PacketWrapper playerInfoRemove = PacketWrapper.create(ClientboundPackets1_21_6.PLAYER_INFO_REMOVE, user());\n            playerInfoRemove.write(Types.UUID_ARRAY, new UUID[]{trackedEntityId.uuid()});\n            playerInfoRemove.send(Protocol1_21_9To1_21_7.class);\n\n            final PacketWrapper removePlayerTeam = PacketWrapper.create(ClientboundPackets1_21_6.SET_PLAYER_TEAM, user());\n            removePlayerTeam.write(Types.STRING, trackedEntityId.name());\n            removePlayerTeam.write(Types.BYTE, (byte) 1); // Method\n            removePlayerTeam.send(Protocol1_21_9To1_21_7.class);\n        }\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21to1_20_5/Protocol1_21To1_20_5.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21to1_20_5;\n\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viabackwards.api.data.BackwardsMappingData;\nimport com.viaversion.viabackwards.api.rewriters.BackwardsRegistryRewriter;\nimport com.viaversion.viabackwards.api.rewriters.text.JsonNBTComponentRewriter;\nimport com.viaversion.viabackwards.protocol.v1_21to1_20_5.rewriter.BlockItemPacketRewriter1_21;\nimport com.viaversion.viabackwards.protocol.v1_21to1_20_5.rewriter.ComponentRewriter1_21;\nimport com.viaversion.viabackwards.protocol.v1_21to1_20_5.rewriter.EntityPacketRewriter1_21;\nimport com.viaversion.viabackwards.protocol.v1_21to1_20_5.storage.EnchantmentsPaintingsStorage;\nimport com.viaversion.viabackwards.protocol.v1_21to1_20_5.storage.OpenScreenStorage;\nimport com.viaversion.viabackwards.protocol.v1_21to1_20_5.storage.PlayerRotationStorage;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.Holder;\nimport com.viaversion.viaversion.api.minecraft.data.version.StructuredDataKeys1_20_5;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_20_5;\nimport com.viaversion.viaversion.api.minecraft.entitydata.types.EntityDataTypes1_20_5;\nimport com.viaversion.viaversion.api.minecraft.item.data.ChatType;\nimport com.viaversion.viaversion.api.protocol.packet.provider.PacketTypesProvider;\nimport com.viaversion.viaversion.api.protocol.packet.provider.SimplePacketTypesProvider;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.chunk.ChunkType1_20_2;\nimport com.viaversion.viaversion.api.type.types.version.Types1_20_5;\nimport com.viaversion.viaversion.api.type.types.version.Types1_21;\nimport com.viaversion.viaversion.api.type.types.version.VersionedTypes;\nimport com.viaversion.viaversion.data.entity.EntityTrackerBase;\nimport com.viaversion.viaversion.protocols.v1_20_3to1_20_5.packet.ClientboundConfigurationPackets1_20_5;\nimport com.viaversion.viaversion.protocols.v1_20_3to1_20_5.packet.ClientboundPacket1_20_5;\nimport com.viaversion.viaversion.protocols.v1_20_3to1_20_5.packet.ClientboundPackets1_20_5;\nimport com.viaversion.viaversion.protocols.v1_20_3to1_20_5.packet.ServerboundConfigurationPackets1_20_5;\nimport com.viaversion.viaversion.protocols.v1_20_3to1_20_5.packet.ServerboundPacket1_20_5;\nimport com.viaversion.viaversion.protocols.v1_20_3to1_20_5.packet.ServerboundPackets1_20_5;\nimport com.viaversion.viaversion.protocols.v1_20_5to1_21.Protocol1_20_5To1_21;\nimport com.viaversion.viaversion.protocols.v1_20_5to1_21.packet.ClientboundConfigurationPackets1_21;\nimport com.viaversion.viaversion.protocols.v1_20_5to1_21.packet.ClientboundPacket1_21;\nimport com.viaversion.viaversion.protocols.v1_20_5to1_21.packet.ClientboundPackets1_21;\nimport com.viaversion.viaversion.rewriter.BlockRewriter;\nimport com.viaversion.viaversion.rewriter.ParticleRewriter;\nimport com.viaversion.viaversion.rewriter.TagRewriter;\nimport com.viaversion.viaversion.util.ArrayUtil;\n\nimport static com.viaversion.viaversion.util.ProtocolUtil.packetTypeMap;\n\npublic final class Protocol1_21To1_20_5 extends BackwardsProtocol<ClientboundPacket1_21, ClientboundPacket1_20_5, ServerboundPacket1_20_5, ServerboundPacket1_20_5> {\n\n    public static final BackwardsMappingData MAPPINGS = new BackwardsMappingData(\"1.21\", \"1.20.5\", Protocol1_20_5To1_21.class);\n    private final EntityPacketRewriter1_21 entityRewriter = new EntityPacketRewriter1_21(this);\n    private final BlockItemPacketRewriter1_21 itemRewriter = new BlockItemPacketRewriter1_21(this);\n    private final ParticleRewriter<ClientboundPacket1_21> particleRewriter = new ParticleRewriter<>(this);\n    private final JsonNBTComponentRewriter<ClientboundPacket1_21> translatableRewriter = new ComponentRewriter1_21(this);\n    private final TagRewriter<ClientboundPacket1_21> tagRewriter = new TagRewriter<>(this);\n    private final BlockRewriter<ClientboundPacket1_21> blockRewriter = BlockRewriter.for1_20_2(this, ChunkType1_20_2::new);\n    private final BackwardsRegistryRewriter registryDataRewriter = new BackwardsRegistryRewriter(this);\n\n    public Protocol1_21To1_20_5() {\n        super(ClientboundPacket1_21.class, ClientboundPacket1_20_5.class, ServerboundPacket1_20_5.class, ServerboundPacket1_20_5.class);\n    }\n\n    @Override\n    protected void registerPackets() {\n        super.registerPackets();\n\n        // Format change we can't properly map - all this really results in is a desync one version earlier\n        cancelClientbound(ClientboundPackets1_21.PROJECTILE_POWER);\n\n        cancelClientbound(ClientboundPackets1_21.CUSTOM_REPORT_DETAILS);\n        cancelClientbound(ClientboundPackets1_21.SERVER_LINKS);\n        cancelClientbound(ClientboundConfigurationPackets1_21.CUSTOM_REPORT_DETAILS);\n        cancelClientbound(ClientboundConfigurationPackets1_21.SERVER_LINKS);\n\n        replaceClientbound(ClientboundPackets1_21.DISGUISED_CHAT, wrapper -> {\n            translatableRewriter.processTag(wrapper.user(), wrapper.passthrough(Types.TRUSTED_TAG)); // Message\n\n            final Holder<ChatType> chatType = wrapper.read(ChatType.TYPE);\n            if (chatType.isDirect()) {\n                // Oh well\n                wrapper.write(Types.VAR_INT, 0);\n                return;\n            }\n\n            wrapper.write(Types.VAR_INT, chatType.id());\n        });\n        replaceClientbound(ClientboundPackets1_21.PLAYER_CHAT, wrapper -> {\n            wrapper.passthrough(Types.UUID); // Sender\n            wrapper.passthrough(Types.VAR_INT); // Index\n            wrapper.passthrough(Types.OPTIONAL_SIGNATURE_BYTES); // Signature\n            wrapper.passthrough(Types.STRING); // Plain content\n            wrapper.passthrough(Types.LONG); // Timestamp\n            wrapper.passthrough(Types.LONG); // Salt\n\n            final int lastSeen = wrapper.passthrough(Types.VAR_INT);\n            for (int i = 0; i < lastSeen; i++) {\n                final int index = wrapper.passthrough(Types.VAR_INT);\n                if (index == 0) {\n                    wrapper.passthrough(Types.SIGNATURE_BYTES);\n                }\n            }\n\n            wrapper.passthrough(Types.TRUSTED_OPTIONAL_TAG); // Unsigned content\n\n            final int filterMaskType = wrapper.passthrough(Types.VAR_INT);\n            if (filterMaskType == 2) {\n                wrapper.passthrough(Types.LONG_ARRAY_PRIMITIVE); // Mask\n            }\n\n            final Holder<ChatType> chatType = wrapper.read(ChatType.TYPE);\n            if (chatType.isDirect()) {\n                // Oh well\n                wrapper.write(Types.VAR_INT, 0);\n            } else {\n                wrapper.write(Types.VAR_INT, chatType.id());\n            }\n\n            translatableRewriter.processTag(wrapper.user(), wrapper.passthrough(Types.TRUSTED_TAG)); // Name\n            translatableRewriter.processTag(wrapper.user(), wrapper.passthrough(Types.TRUSTED_OPTIONAL_TAG)); // Target Name\n        });\n\n        replaceClientbound(ClientboundPackets1_21.UPDATE_ATTRIBUTES, wrapper -> {\n            wrapper.passthrough(Types.VAR_INT); // Entity ID\n\n            final int size = wrapper.passthrough(Types.VAR_INT);\n            int newSize = size;\n            for (int i = 0; i < size; i++) {\n                final int attributeId = wrapper.read(Types.VAR_INT);\n                final int mappedId = MAPPINGS.getNewAttributeId(attributeId);\n                if (mappedId == -1) {\n                    newSize--;\n\n                    wrapper.read(Types.DOUBLE); // Base\n                    final int modifierSize = wrapper.read(Types.VAR_INT);\n                    for (int j = 0; j < modifierSize; j++) {\n                        wrapper.read(Types.STRING); // ID\n                        wrapper.read(Types.DOUBLE); // Amount\n                        wrapper.read(Types.BYTE); // Operation\n                    }\n                    continue;\n                }\n\n                wrapper.write(Types.VAR_INT, mappedId);\n                wrapper.passthrough(Types.DOUBLE); // Base\n                final int modifierSize = wrapper.passthrough(Types.VAR_INT);\n                for (int j = 0; j < modifierSize; j++) {\n                    final String id = wrapper.read(Types.STRING);\n                    wrapper.write(Types.UUID, Protocol1_20_5To1_21.mapAttributeId(id));\n                    wrapper.passthrough(Types.DOUBLE); // Amount\n                    wrapper.passthrough(Types.BYTE); // Operation\n                }\n            }\n\n            if (size != newSize) {\n                wrapper.set(Types.VAR_INT, 1, newSize);\n            }\n        });\n\n        registerClientbound(ClientboundConfigurationPackets1_21.UPDATE_ENABLED_FEATURES, wrapper -> {\n            final String[] enabledFeatures = wrapper.read(Types.STRING_ARRAY);\n            wrapper.write(Types.STRING_ARRAY, ArrayUtil.add(enabledFeatures, \"minecraft:update_1_21\"));\n        });\n    }\n\n    @Override\n    public void init(final UserConnection user) {\n        addEntityTracker(user, new EntityTrackerBase(user, EntityTypes1_20_5.PLAYER));\n        user.put(new EnchantmentsPaintingsStorage());\n        user.put(new OpenScreenStorage());\n        user.put(new PlayerRotationStorage());\n    }\n\n    @Override\n    public BackwardsMappingData getMappingData() {\n        return MAPPINGS;\n    }\n\n    @Override\n    public EntityPacketRewriter1_21 getEntityRewriter() {\n        return entityRewriter;\n    }\n\n    @Override\n    public BlockItemPacketRewriter1_21 getItemRewriter() {\n        return itemRewriter;\n    }\n\n    @Override\n    public BlockRewriter<ClientboundPacket1_21> getBlockRewriter() {\n        return blockRewriter;\n    }\n\n    @Override\n    public BackwardsRegistryRewriter getRegistryDataRewriter() {\n        return registryDataRewriter;\n    }\n\n    @Override\n    public ParticleRewriter<ClientboundPacket1_21> getParticleRewriter() {\n        return particleRewriter;\n    }\n\n    @Override\n    public JsonNBTComponentRewriter<ClientboundPacket1_21> getComponentRewriter() {\n        return translatableRewriter;\n    }\n\n    @Override\n    public TagRewriter<ClientboundPacket1_21> getTagRewriter() {\n        return tagRewriter;\n    }\n\n    @Override\n    public Types1_21 types() {\n        return VersionedTypes.V1_21;\n    }\n\n    @Override\n    public Types1_20_5<StructuredDataKeys1_20_5, EntityDataTypes1_20_5> mappedTypes() {\n        return VersionedTypes.V1_20_5;\n    }\n\n    @Override\n    protected PacketTypesProvider<ClientboundPacket1_21, ClientboundPacket1_20_5, ServerboundPacket1_20_5, ServerboundPacket1_20_5> createPacketTypesProvider() {\n        return new SimplePacketTypesProvider<>(\n            packetTypeMap(unmappedClientboundPacketType, ClientboundPackets1_21.class, ClientboundConfigurationPackets1_21.class),\n            packetTypeMap(mappedClientboundPacketType, ClientboundPackets1_20_5.class, ClientboundConfigurationPackets1_20_5.class),\n            packetTypeMap(mappedServerboundPacketType, ServerboundPackets1_20_5.class, ServerboundConfigurationPackets1_20_5.class),\n            packetTypeMap(unmappedServerboundPacketType, ServerboundPackets1_20_5.class, ServerboundConfigurationPackets1_20_5.class)\n        );\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21to1_20_5/rewriter/BlockItemPacketRewriter1_21.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21to1_20_5.rewriter;\n\nimport com.viaversion.nbt.tag.ByteTag;\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.ListTag;\nimport com.viaversion.nbt.tag.StringTag;\nimport com.viaversion.nbt.tag.Tag;\nimport com.viaversion.viabackwards.api.rewriters.BackwardsStructuredItemRewriter;\nimport com.viaversion.viabackwards.api.rewriters.EnchantmentRewriter;\nimport com.viaversion.viabackwards.api.rewriters.StructuredEnchantmentRewriter;\nimport com.viaversion.viabackwards.protocol.v1_21to1_20_5.Protocol1_21To1_20_5;\nimport com.viaversion.viabackwards.protocol.v1_21to1_20_5.storage.EnchantmentsPaintingsStorage;\nimport com.viaversion.viabackwards.protocol.v1_21to1_20_5.storage.OpenScreenStorage;\nimport com.viaversion.viabackwards.protocol.v1_21to1_20_5.storage.PlayerRotationStorage;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.EitherHolder;\nimport com.viaversion.viaversion.api.minecraft.Holder;\nimport com.viaversion.viaversion.api.minecraft.SoundEvent;\nimport com.viaversion.viaversion.api.minecraft.data.StructuredDataContainer;\nimport com.viaversion.viaversion.api.minecraft.data.StructuredDataKey;\nimport com.viaversion.viaversion.api.minecraft.item.Item;\nimport com.viaversion.viaversion.api.minecraft.item.data.AttributeModifiers1_21;\nimport com.viaversion.viaversion.api.minecraft.item.data.AttributeModifiers1_21.AttributeModifier;\nimport com.viaversion.viaversion.api.minecraft.item.data.AttributeModifiers1_21.ModifierData;\nimport com.viaversion.viaversion.api.minecraft.item.data.Enchantments;\nimport com.viaversion.viaversion.api.minecraft.item.data.JukeboxPlayable;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.chunk.ChunkType1_20_2;\nimport com.viaversion.viaversion.libs.fastutil.ints.Int2IntMap;\nimport com.viaversion.viaversion.libs.mcstructs.text.TextFormatting;\nimport com.viaversion.viaversion.libs.mcstructs.text.components.StringComponent;\nimport com.viaversion.viaversion.libs.mcstructs.text.components.TranslationComponent;\nimport com.viaversion.viaversion.protocols.v1_20_2to1_20_3.rewriter.RecipeRewriter1_20_3;\nimport com.viaversion.viaversion.protocols.v1_20_3to1_20_5.data.Enchantments1_20_5;\nimport com.viaversion.viaversion.protocols.v1_20_3to1_20_5.packet.ServerboundPacket1_20_5;\nimport com.viaversion.viaversion.protocols.v1_20_3to1_20_5.packet.ServerboundPackets1_20_5;\nimport com.viaversion.viaversion.protocols.v1_20_5to1_21.packet.ClientboundPacket1_21;\nimport com.viaversion.viaversion.protocols.v1_20_5to1_21.packet.ClientboundPackets1_21;\nimport com.viaversion.viaversion.rewriter.BlockRewriter;\nimport com.viaversion.viaversion.rewriter.IdRewriteFunction;\nimport com.viaversion.viaversion.util.SerializerVersion;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport static com.viaversion.viaversion.protocols.v1_20_5to1_21.rewriter.BlockItemPacketRewriter1_21.downgradeItemData;\nimport static com.viaversion.viaversion.protocols.v1_20_5to1_21.rewriter.BlockItemPacketRewriter1_21.updateItemData;\n\npublic final class BlockItemPacketRewriter1_21 extends BackwardsStructuredItemRewriter<ClientboundPacket1_21, ServerboundPacket1_20_5, Protocol1_21To1_20_5> {\n\n    private final StructuredEnchantmentRewriter enchantmentRewriter = new StructuredEnchantmentRewriter(this);\n\n    public BlockItemPacketRewriter1_21(final Protocol1_21To1_20_5 protocol) {\n        super(protocol);\n    }\n\n    @Override\n    public void registerPackets() {\n        protocol.replaceClientbound(ClientboundPackets1_21.OPEN_SCREEN, wrapper -> {\n            wrapper.passthrough(Types.VAR_INT); // Id\n\n            // Tracking the type actually matters now with crafters also using container data above index 3\n            final int menuType = wrapper.passthrough(Types.VAR_INT);\n            wrapper.user().get(OpenScreenStorage.class).setMenuType(menuType);\n\n            protocol.getComponentRewriter().passthroughAndProcess(wrapper);\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_21.CONTAINER_SET_DATA, wrapper -> {\n            wrapper.passthrough(Types.UNSIGNED_BYTE); // Container id\n            final short property = wrapper.passthrough(Types.SHORT);\n            if (property >= 4 && property <= 6) { // Enchantment hints\n                final OpenScreenStorage openScreenStorage = wrapper.user().get(OpenScreenStorage.class);\n                if (openScreenStorage.menuType() != 13) { // Enchantment table\n                    return;\n                }\n\n                final short enchantmentId = wrapper.read(Types.SHORT);\n                final EnchantmentsPaintingsStorage storage = wrapper.user().get(EnchantmentsPaintingsStorage.class);\n                final String key = storage.enchantments().idToKey(enchantmentId);\n                final int mappedId = key != null ? Enchantments1_20_5.keyToId(key) : -1;\n                wrapper.write(Types.SHORT, (short) mappedId);\n            }\n        });\n\n        protocol.registerClientbound(ClientboundPackets1_21.HORSE_SCREEN_OPEN, wrapper -> {\n            wrapper.passthrough(Types.UNSIGNED_BYTE); // Container id\n\n            // From columns to size\n            final int columns = wrapper.read(Types.VAR_INT);\n            wrapper.write(Types.VAR_INT, columns * 3 + 1);\n        });\n\n        protocol.replaceClientbound(ClientboundPackets1_21.LEVEL_EVENT, wrapper -> {\n            final int event = wrapper.passthrough(Types.INT);\n            wrapper.passthrough(Types.BLOCK_POSITION1_14);\n\n            final int data = wrapper.read(Types.INT);\n            if (event == 1010) {\n                final int itemId = wrapper.user().get(EnchantmentsPaintingsStorage.class).jubeboxSongToItem(data);\n                if (itemId == -1) {\n                    wrapper.cancel();\n                    return;\n                }\n\n                wrapper.write(Types.INT, itemId);\n            } else if (event == 2001) {\n                wrapper.write(Types.INT, protocol.getMappingData().getNewBlockStateId(data));\n            } else {\n                wrapper.write(Types.INT, data);\n            }\n        });\n\n        protocol.registerServerbound(ServerboundPackets1_20_5.USE_ITEM, wrapper -> {\n            wrapper.passthrough(Types.VAR_INT); // Hand\n            wrapper.passthrough(Types.VAR_INT); // Sequence\n\n            final PlayerRotationStorage rotation = wrapper.user().get(PlayerRotationStorage.class);\n            wrapper.write(Types.FLOAT, rotation.yaw());\n            wrapper.write(Types.FLOAT, rotation.pitch());\n        });\n\n        new RecipeRewriter1_20_3<>(protocol).register1_20_5(ClientboundPackets1_21.UPDATE_RECIPES);\n    }\n\n    @Override\n    public Item handleItemToClient(final UserConnection connection, Item item) {\n        if (item.isEmpty()) {\n            return item;\n        }\n\n        final StructuredDataContainer data = item.dataContainer();\n        data.setIdLookup(protocol, true);\n\n        // Enchantments\n        final EnchantmentsPaintingsStorage storage = connection.get(EnchantmentsPaintingsStorage.class);\n        final IdRewriteFunction idRewriteFunction = id -> {\n            final String key = storage.enchantments().idToKey(id);\n            return key != null ? Enchantments1_20_5.keyToId(key) : -1;\n        };\n        final StructuredEnchantmentRewriter.DescriptionSupplier descriptionSupplier = (id, level) -> {\n            final Tag description = storage.enchantmentDescription(id);\n            if (description == null) {\n                return new StringTag(\"Unknown enchantment\");\n            }\n\n            final var component = SerializerVersion.V1_20_5.toComponent(description);\n            component.getStyle().setItalic(false);\n            component.getStyle().setFormatting(TextFormatting.GRAY);\n            if (level != 1 || storage.enchantmentMaxLevel(id) != 1) {\n                component.getSiblings().add(new StringComponent(\" \"));\n                component.getSiblings().add(new TranslationComponent(EnchantmentRewriter.ENCHANTMENT_LEVEL_TRANSLATION.formatted(level)));\n            }\n\n            return SerializerVersion.V1_20_5.toTag(component);\n        };\n        enchantmentRewriter.rewriteEnchantmentsToClient(data, StructuredDataKey.ENCHANTMENTS1_20_5, idRewriteFunction, descriptionSupplier, false);\n        enchantmentRewriter.rewriteEnchantmentsToClient(data, StructuredDataKey.STORED_ENCHANTMENTS1_20_5, idRewriteFunction, descriptionSupplier, true);\n\n        final int identifier = item.identifier();\n\n        // Order is important\n        backupInconvertibleData(item);\n        item = super.handleItemToClient(connection, item);\n        downgradeItemData(item);\n\n        if (data.has(StructuredDataKey.RARITY)) {\n            return item;\n        }\n        // Change rarity of trident and piglin banner pattern\n        final boolean trident = identifier == 1188;\n        if (trident || identifier == 1200) {\n            data.set(StructuredDataKey.RARITY, trident ? 3 : 1); // Epic or Uncommon\n            saveTag(createCustomTag(item), new ByteTag(true), \"rarity\");\n        }\n        return item;\n    }\n\n    @Override\n    public Item handleItemToServer(final UserConnection connection, Item item) {\n        if (item.isEmpty()) {\n            return item;\n        }\n\n        final StructuredDataContainer data = item.dataContainer();\n        data.setIdLookup(protocol, false);\n\n        // Rewrite enchantments\n        final EnchantmentsPaintingsStorage storage = connection.get(EnchantmentsPaintingsStorage.class);\n        rewriteEnchantmentToServer(storage, item, StructuredDataKey.ENCHANTMENTS1_20_5);\n        rewriteEnchantmentToServer(storage, item, StructuredDataKey.STORED_ENCHANTMENTS1_20_5);\n\n        // Restore originals if present\n        enchantmentRewriter.handleToServer(item);\n\n        // Order is important\n        item = super.handleItemToServer(connection, item);\n        updateItemData(item);\n        restoreInconvertibleData(item);\n\n        final CompoundTag customData = data.get(StructuredDataKey.CUSTOM_DATA);\n        if (customData == null) {\n            return item;\n        }\n        if (customData.remove(nbtTagName(\"rarity\")) != null) {\n            data.remove(StructuredDataKey.RARITY);\n            removeCustomTag(data, customData);\n        }\n        return item;\n    }\n\n    private void rewriteEnchantmentToServer(final EnchantmentsPaintingsStorage storage, final Item item, final StructuredDataKey<Enchantments> key) {\n        final Enchantments enchantments = item.dataContainer().get(key);\n        if (enchantments == null) {\n            return;\n        }\n\n        final List<PendingIdChange> updatedIds = new ArrayList<>();\n        for (final Int2IntMap.Entry entry : enchantments.enchantments().int2IntEntrySet()) {\n            final int id = entry.getIntKey();\n            final String enchantmentKey = Enchantments1_20_5.idToKey(id);\n            if (enchantmentKey == null) {\n                continue;\n            }\n\n            final int mappedId = storage.enchantments().keyToId(enchantmentKey);\n            if (id != mappedId) {\n                final int level = entry.getIntValue();\n                updatedIds.add(new PendingIdChange(id, mappedId, level));\n            }\n        }\n\n        // Remove first, then add updated entries\n        for (final PendingIdChange change : updatedIds) {\n            enchantments.remove(change.id);\n        }\n        for (final PendingIdChange change : updatedIds) {\n            enchantments.add(change.mappedId, change.level);\n        }\n    }\n\n    private void backupInconvertibleData(final Item item) {\n        final StructuredDataContainer data = item.dataContainer();\n        data.setIdLookup(protocol, true);\n\n        final CompoundTag backupTag = new CompoundTag();\n\n        final JukeboxPlayable jukeboxPlayable = data.get(StructuredDataKey.JUKEBOX_PLAYABLE1_21);\n        if (jukeboxPlayable != null) {\n            final CompoundTag tag = new CompoundTag();\n            if (jukeboxPlayable.song().hasHolder()) {\n                final Holder<JukeboxPlayable.JukeboxSong> songHolder = jukeboxPlayable.song().holder();\n                tag.put(\"song\", holderToTag(songHolder, (song, songTag) -> {\n                    saveSoundEventHolder(songTag, song.soundEvent());\n                    songTag.put(\"description\", song.description());\n                    songTag.putFloat(\"length_in_seconds\", song.lengthInSeconds());\n                    songTag.putInt(\"comparator_output\", song.comparatorOutput());\n                }));\n            } else {\n                tag.putString(\"song_identifier\", jukeboxPlayable.song().key());\n            }\n            tag.putBoolean(\"show_in_tooltip\", jukeboxPlayable.showInTooltip());\n\n            backupTag.put(\"jukebox_playable\", tag);\n        }\n\n        final AttributeModifiers1_21 attributeModifiers = data.get(StructuredDataKey.ATTRIBUTE_MODIFIERS1_21);\n        if (attributeModifiers != null) {\n            final ListTag<StringTag> attributeIds = new ListTag<>(StringTag.class);\n            for (final AttributeModifier modifier : attributeModifiers.modifiers()) {\n                attributeIds.add(new StringTag(modifier.modifier().id()));\n            }\n            backupTag.put(\"attribute_modifiers\", attributeIds);\n        }\n\n        if (!backupTag.isEmpty()) {\n            saveTag(createCustomTag(item), backupTag, \"inconvertible_data\");\n        }\n    }\n\n    private void restoreInconvertibleData(final Item item) {\n        final StructuredDataContainer data = item.dataContainer();\n        final CompoundTag customData = data.get(StructuredDataKey.CUSTOM_DATA);\n        if (customData == null || !(customData.remove(nbtTagName(\"inconvertible_data\")) instanceof CompoundTag tag)) {\n            return;\n        }\n\n        final CompoundTag jukeboxPlayableTag = tag.getCompoundTag(\"jukebox_playable\");\n        if (jukeboxPlayableTag != null) {\n            final EitherHolder<JukeboxPlayable.JukeboxSong> song;\n            final String songIdentifier = tag.getString(\"song_identifier\");\n            if (songIdentifier != null) {\n                song = EitherHolder.of(songIdentifier);\n            } else {\n                song = EitherHolder.of(restoreHolder(jukeboxPlayableTag, \"song\", songTag -> {\n                    final Holder<SoundEvent> soundEvent = restoreSoundEventHolder(songTag);\n                    final Tag description = songTag.get(\"description\");\n                    final float lengthInSeconds = songTag.getFloat(\"length_in_seconds\");\n                    final int comparatorOutput = songTag.getInt(\"comparator_output\");\n                    return new JukeboxPlayable.JukeboxSong(soundEvent, description, lengthInSeconds, comparatorOutput);\n                }));\n            }\n\n            final JukeboxPlayable jukeboxPlayable = new JukeboxPlayable(song, tag.getBoolean(\"show_in_tooltip\"));\n            data.set(StructuredDataKey.JUKEBOX_PLAYABLE1_21, jukeboxPlayable);\n        }\n\n        final ListTag<StringTag> attributeIds = tag.getListTag(\"attribute_modifiers\", StringTag.class);\n        final AttributeModifiers1_21 attributeModifiers = data.get(StructuredDataKey.ATTRIBUTE_MODIFIERS1_21);\n        if (attributeIds != null && attributeModifiers != null && attributeIds.size() == attributeModifiers.modifiers().length) {\n            for (int i = 0; i < attributeIds.size(); i++) {\n                final String id = attributeIds.get(i).getValue();\n                final AttributeModifier modifier = attributeModifiers.modifiers()[i];\n                final ModifierData updatedModifierData = new ModifierData(id, modifier.modifier().amount(), modifier.modifier().operation());\n                attributeModifiers.modifiers()[i] = new AttributeModifier(modifier.attribute(), updatedModifierData, modifier.slotType());\n            }\n        }\n\n        removeCustomTag(data, customData);\n    }\n\n    private record PendingIdChange(int id, int mappedId, int level) {\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21to1_20_5/rewriter/ComponentRewriter1_21.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21to1_20_5.rewriter;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.IntArrayTag;\nimport com.viaversion.nbt.tag.ListTag;\nimport com.viaversion.viabackwards.api.rewriters.text.JsonNBTComponentRewriter;\nimport com.viaversion.viabackwards.protocol.v1_21to1_20_5.Protocol1_21To1_20_5;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.data.StructuredDataKey;\nimport com.viaversion.viaversion.protocols.v1_20_3to1_20_5.data.Attributes1_20_5;\nimport com.viaversion.viaversion.protocols.v1_20_5to1_21.Protocol1_20_5To1_21;\nimport com.viaversion.viaversion.protocols.v1_20_5to1_21.data.AttributeModifierMappings1_21;\nimport com.viaversion.viaversion.protocols.v1_20_5to1_21.packet.ClientboundPacket1_21;\nimport com.viaversion.viaversion.util.Key;\nimport com.viaversion.viaversion.util.SerializerVersion;\nimport com.viaversion.viaversion.util.TagUtil;\nimport com.viaversion.viaversion.util.UUIDUtil;\nimport java.util.UUID;\n\npublic final class ComponentRewriter1_21 extends JsonNBTComponentRewriter<ClientboundPacket1_21> {\n\n    public ComponentRewriter1_21(final Protocol1_21To1_20_5 protocol) {\n        super(protocol, ReadType.NBT);\n    }\n\n    private void convertAttributeModifiersComponent(final CompoundTag tag) {\n        final CompoundTag attributeModifiers = TagUtil.getNamespacedCompoundTag(tag, \"attribute_modifiers\");\n        if (attributeModifiers == null) {\n            return;\n        }\n        final ListTag<CompoundTag> modifiers = attributeModifiers.getListTag(\"modifiers\", CompoundTag.class);\n        int size = modifiers.size();\n        for (int i = 0; i < size; i++) {\n            final CompoundTag modifier = modifiers.get(i);\n            final String type = Key.stripMinecraftNamespace(modifier.getString(\"type\"));\n            if (Attributes1_20_5.keyToId(type) == -1) {\n                // Ignore new attributes\n                modifiers.remove(i--);\n                size--;\n                continue;\n            }\n\n            final String id = modifier.getString(\"id\");\n            final UUID uuid = Protocol1_20_5To1_21.mapAttributeId(id);\n            final String name = AttributeModifierMappings1_21.idToName(id);\n            modifier.put(\"uuid\", new IntArrayTag(UUIDUtil.toIntArray(uuid)));\n            modifier.putString(\"name\", name != null ? name : id);\n        }\n    }\n\n    @Override\n    protected void handleShowItem(final UserConnection connection, final CompoundTag itemTag, final CompoundTag componentsTag) {\n        super.handleShowItem(connection, itemTag, componentsTag);\n        if (componentsTag != null) {\n            removeDataComponents(componentsTag, StructuredDataKey.JUKEBOX_PLAYABLE1_21);\n            convertAttributeModifiersComponent(componentsTag);\n        }\n    }\n\n    @Override\n    protected SerializerVersion inputSerializerVersion() {\n        return SerializerVersion.V1_20_5;\n    }\n\n    @Override\n    protected SerializerVersion outputSerializerVersion() {\n        return SerializerVersion.V1_20_5;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21to1_20_5/rewriter/EntityPacketRewriter1_21.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21to1_20_5.rewriter;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.Tag;\nimport com.viaversion.viabackwards.api.rewriters.EntityRewriter;\nimport com.viaversion.viabackwards.protocol.v1_21to1_20_5.Protocol1_21To1_20_5;\nimport com.viaversion.viabackwards.protocol.v1_21to1_20_5.storage.EnchantmentsPaintingsStorage;\nimport com.viaversion.viabackwards.protocol.v1_21to1_20_5.storage.PlayerRotationStorage;\nimport com.viaversion.viaversion.api.minecraft.Holder;\nimport com.viaversion.viaversion.api.minecraft.PaintingVariant;\nimport com.viaversion.viaversion.api.minecraft.RegistryEntry;\nimport com.viaversion.viaversion.api.minecraft.WolfVariant;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityType;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_20_5;\nimport com.viaversion.viaversion.api.minecraft.entitydata.types.EntityDataTypes1_20_5;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.version.VersionedTypes;\nimport com.viaversion.viaversion.protocols.v1_20_3to1_20_5.packet.ServerboundPackets1_20_5;\nimport com.viaversion.viaversion.protocols.v1_20_5to1_21.data.Paintings1_20_5;\nimport com.viaversion.viaversion.protocols.v1_20_5to1_21.packet.ClientboundConfigurationPackets1_21;\nimport com.viaversion.viaversion.protocols.v1_20_5to1_21.packet.ClientboundPacket1_21;\nimport com.viaversion.viaversion.util.Key;\nimport com.viaversion.viaversion.util.KeyMappings;\nimport java.util.HashMap;\nimport java.util.Map;\n\npublic final class EntityPacketRewriter1_21 extends EntityRewriter<ClientboundPacket1_21, Protocol1_21To1_20_5> {\n\n    private final Map<String, PaintingData> oldPaintings = new HashMap<>();\n\n    public EntityPacketRewriter1_21(final Protocol1_21To1_20_5 protocol) {\n        super(protocol, protocol.mappedTypes().entityDataTypes().optionalComponentType, protocol.mappedTypes().entityDataTypes().booleanType);\n\n        for (int i = 0; i < Paintings1_20_5.PAINTINGS.length; i++) {\n            final PaintingVariant painting = Paintings1_20_5.PAINTINGS[i];\n            oldPaintings.put(painting.assetId(), new PaintingData(painting, i));\n        }\n    }\n\n    @Override\n    public void registerPackets() {\n        protocol.replaceClientbound(ClientboundConfigurationPackets1_21.REGISTRY_DATA, wrapper -> {\n            final String key = Key.stripMinecraftNamespace(wrapper.passthrough(Types.STRING));\n            final RegistryEntry[] entries = wrapper.passthrough(Types.REGISTRY_ENTRY_ARRAY);\n            final boolean paintingVariant = key.equals(\"painting_variant\");\n            final boolean enchantment = key.equals(\"enchantment\");\n            if (paintingVariant || enchantment || key.equals(\"jukebox_song\")) {\n                // Track custom registries and cancel the packet\n                final String[] keys = new String[entries.length];\n                for (int i = 0; i < entries.length; i++) {\n                    keys[i] = Key.stripMinecraftNamespace(entries[i].key());\n                }\n\n                final EnchantmentsPaintingsStorage storage = wrapper.user().get(EnchantmentsPaintingsStorage.class);\n                if (paintingVariant) {\n                    storage.setPaintings(new KeyMappings(keys), paintingMappingsForEntries(entries));\n                } else if (enchantment) {\n                    final Tag[] descriptions = new Tag[entries.length];\n                    final int[] maxLevels = new int[entries.length];\n                    for (int i = 0; i < entries.length; i++) {\n                        final RegistryEntry entry = entries[i];\n                        if (entry.tag() instanceof final CompoundTag tag) {\n                            descriptions[i] = tag.get(\"description\");\n                            maxLevels[i] = tag.getInt(\"max_level\");\n                        }\n                    }\n                    storage.setEnchantments(new KeyMappings(keys), descriptions, maxLevels);\n                } else {\n                    final int[] jukeboxSongMappings = new int[keys.length];\n                    for (int i = 0; i < keys.length; i++) {\n                        final int itemId = protocol.getMappingData().getFullItemMappings().mappedId(\"music_disc_\" + keys[i]);\n                        jukeboxSongMappings[i] = itemId;\n                    }\n                    storage.setJubeboxSongsToItems(jukeboxSongMappings);\n                }\n\n                wrapper.cancel();\n            } else {\n                protocol.getRegistryDataRewriter().trackDimensionAndBiomes(wrapper.user(), key, entries);\n            }\n        });\n\n        protocol.registerServerbound(ServerboundPackets1_20_5.MOVE_PLAYER_POS_ROT, wrapper -> {\n            wrapper.passthrough(Types.DOUBLE); // X\n            wrapper.passthrough(Types.DOUBLE); // Y\n            wrapper.passthrough(Types.DOUBLE); // Z\n\n            storePlayerRotation(wrapper);\n        });\n\n        protocol.registerServerbound(ServerboundPackets1_20_5.MOVE_PLAYER_ROT, this::storePlayerRotation);\n    }\n\n    private void storePlayerRotation(final PacketWrapper wrapper) {\n        final float yaw = wrapper.passthrough(Types.FLOAT);\n        final float pitch = wrapper.passthrough(Types.FLOAT);\n\n        wrapper.user().get(PlayerRotationStorage.class).setRotation(yaw, pitch);\n    }\n\n    private int[] paintingMappingsForEntries(final RegistryEntry[] entries) {\n        final int[] mappings = new int[entries.length];\n        for (int i = 0; i < entries.length; i++) {\n            final RegistryEntry entry = entries[i];\n            final PaintingData paintingData = oldPaintings.get(Key.stripMinecraftNamespace(entry.key()));\n            if (paintingData != null) {\n                mappings[i] = paintingData.id;\n                continue;\n            }\n\n            // Figure out which works by size\n            if (entry.tag() == null) {\n                continue;\n            }\n\n            final CompoundTag tag = (CompoundTag) entry.tag();\n            for (int j = 0; j < Paintings1_20_5.PAINTINGS.length; j++) {\n                final PaintingVariant painting = Paintings1_20_5.PAINTINGS[j];\n                if (painting.width() == tag.getInt(\"width\") && painting.height() == tag.getInt(\"height\")) {\n                    mappings[i] = j;\n                    break;\n                }\n            }\n        }\n        return mappings;\n    }\n\n    @Override\n    protected void registerRewrites() {\n        final EntityDataTypes1_20_5 mappedEntityDataTypes = VersionedTypes.V1_20_5.entityDataTypes;\n        dataTypeMapper()\n            .skip(VersionedTypes.V1_21.entityDataTypes.wolfVariantType)\n            .skip(VersionedTypes.V1_21.entityDataTypes.paintingVariantType)\n            .register();\n\n        filter().dataType(VersionedTypes.V1_21.entityDataTypes.wolfVariantType).handler((event, data) -> {\n            final Holder<WolfVariant> variant = data.value();\n            if (variant.hasId()) {\n                data.setTypeAndValue(mappedEntityDataTypes.wolfVariantType, variant.id());\n            } else {\n                event.cancel();\n            }\n        });\n        filter().dataType(VersionedTypes.V1_21.entityDataTypes.paintingVariantType).handler((event, data) -> {\n            final Holder<PaintingVariant> variant = data.value();\n            if (variant.hasId()) {\n                final EnchantmentsPaintingsStorage storage = event.user().get(EnchantmentsPaintingsStorage.class);\n                final int mappedId = storage.mappedPainting(variant.id());\n                data.setTypeAndValue(mappedEntityDataTypes.paintingVariantType, mappedId);\n            } else {\n                event.cancel();\n            }\n        });\n        registerEntityDataTypeHandler1_20_3(\n            mappedEntityDataTypes.itemType,\n            mappedEntityDataTypes.blockStateType,\n            mappedEntityDataTypes.optionalBlockStateType,\n            mappedEntityDataTypes.particleType,\n            mappedEntityDataTypes.particlesType,\n            mappedEntityDataTypes.componentType,\n            mappedEntityDataTypes.optionalComponentType\n        );\n        registerBlockStateHandler(EntityTypes1_20_5.ABSTRACT_MINECART, 11);\n    }\n\n    @Override\n    public EntityType typeFromId(final int type) {\n        return EntityTypes1_20_5.getTypeFromId(type);\n    }\n\n    private record PaintingData(PaintingVariant painting, int id) {\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21to1_20_5/storage/EnchantmentsPaintingsStorage.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21to1_20_5.storage;\n\nimport com.viaversion.nbt.tag.Tag;\nimport com.viaversion.viaversion.api.connection.StorableObject;\nimport com.viaversion.viaversion.util.KeyMappings;\nimport org.checkerframework.checker.nullness.qual.Nullable;\n\npublic final class EnchantmentsPaintingsStorage implements StorableObject {\n    private KeyMappings enchantments;\n    private KeyMappings paintings;\n    private int[] paintingMappings;\n    private Tag[] enchantmentDescriptions;\n    private int[] enchantmentMaxLevels;\n    private int[] jubeboxSongsToItems;\n\n    public KeyMappings enchantments() {\n        return enchantments;\n    }\n\n    public void setEnchantments(final KeyMappings enchantment, final Tag[] enchantmentDescriptions, final int[] enchantmentMaxLevels) {\n        this.enchantments = enchantment;\n        this.enchantmentDescriptions = enchantmentDescriptions;\n        this.enchantmentMaxLevels = enchantmentMaxLevels;\n    }\n\n    public KeyMappings paintings() {\n        return paintings;\n    }\n\n    public void setPaintings(final KeyMappings paintings, final int[] paintingMappings) {\n        this.paintings = paintings;\n        this.paintingMappings = paintingMappings;\n    }\n\n    public void setJubeboxSongsToItems(final int[] jubeboxSongsToItems) {\n        this.jubeboxSongsToItems = jubeboxSongsToItems;\n    }\n\n    public int jubeboxSongToItem(final int id) {\n        return id >= 0 && id < jubeboxSongsToItems.length ? jubeboxSongsToItems[id] : -1;\n    }\n\n    public int mappedPainting(final int id) {\n        return id > 0 && id < paintingMappings.length ? paintingMappings[id] : 0;\n    }\n\n    public @Nullable Tag enchantmentDescription(final int id) {\n        return id > 0 && id < enchantmentDescriptions.length ? enchantmentDescriptions[id] : null;\n    }\n\n    public int enchantmentMaxLevel(final int id) {\n        return id > 0 && id < enchantmentMaxLevels.length ? enchantmentMaxLevels[id] : -1;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21to1_20_5/storage/OpenScreenStorage.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21to1_20_5.storage;\n\nimport com.viaversion.viaversion.api.connection.StorableObject;\n\npublic final class OpenScreenStorage implements StorableObject {\n\n    private int menuType = -1;\n\n    public int menuType() {\n        return menuType;\n    }\n\n    public void setMenuType(final int menuType) {\n        this.menuType = menuType;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_21to1_20_5/storage/PlayerRotationStorage.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_21to1_20_5.storage;\n\nimport com.viaversion.viaversion.api.connection.StorableObject;\n\npublic final class PlayerRotationStorage implements StorableObject {\n\n    private float yaw, pitch;\n\n    public void setRotation(final float yaw, final float pitch) {\n        this.yaw = yaw;\n        this.pitch = pitch;\n    }\n\n    public float yaw() {\n        return yaw;\n    }\n\n    public float pitch() {\n        return pitch;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_9_1to1_9/Protocol1_9_1To1_9.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_9_1to1_9;\n\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viabackwards.api.rewriters.text.JsonNBTComponentRewriter;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.protocols.v1_8to1_9.packet.ClientboundPackets1_9;\nimport com.viaversion.viaversion.protocols.v1_8to1_9.packet.ServerboundPackets1_9;\nimport com.viaversion.viaversion.rewriter.text.ComponentRewriterBase;\n\npublic class Protocol1_9_1To1_9 extends BackwardsProtocol<ClientboundPackets1_9, ClientboundPackets1_9, ServerboundPackets1_9, ServerboundPackets1_9> {\n\n    public Protocol1_9_1To1_9() {\n        super(ClientboundPackets1_9.class, ClientboundPackets1_9.class, ServerboundPackets1_9.class, ServerboundPackets1_9.class);\n    }\n\n    @Override\n    protected void registerPackets() {\n        registerClientbound(ClientboundPackets1_9.LOGIN, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.INT); // 0 - Player ID\n                map(Types.UNSIGNED_BYTE); // 1 - Player Gamemode\n                // 1.9.1 PRE 2 Changed this\n                map(Types.INT, Types.BYTE); // 2 - Player Dimension\n                map(Types.UNSIGNED_BYTE); // 3 - World Difficulty\n                map(Types.UNSIGNED_BYTE); // 4 - Max Players (Tab)\n                map(Types.STRING); // 5 - Level Type\n                map(Types.BOOLEAN); // 6 - Reduced Debug info\n            }\n        });\n\n        registerClientbound(ClientboundPackets1_9.SOUND, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.VAR_INT); // 0 - Sound ID\n\n                handler(wrapper -> {\n                    int sound = wrapper.get(Types.VAR_INT, 0);\n\n                    if (sound == 415) // Stop the Elytra sound for 1.9 (It's introduced in 1.9.2)\n                        wrapper.cancel();\n                    else if (sound >= 416) // Act like the Elytra sound never existed\n                        wrapper.set(Types.VAR_INT, 0, sound - 1);\n                });\n            }\n        });\n\n        JsonNBTComponentRewriter<ClientboundPackets1_9> componentRewriter = new JsonNBTComponentRewriter<>(this, ComponentRewriterBase.ReadType.JSON);\n        componentRewriter.registerComponentPacket(ClientboundPackets1_9.CHAT);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_9_3to1_9_1/Protocol1_9_3To1_9_1.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_9_3to1_9_1;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.StringTag;\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viabackwards.api.rewriters.text.JsonNBTComponentRewriter;\nimport com.viaversion.viabackwards.protocol.v1_9_3to1_9_1.data.BlockEntity1_9_1;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.BlockPosition;\nimport com.viaversion.viaversion.api.minecraft.ClientWorld;\nimport com.viaversion.viaversion.api.minecraft.chunks.Chunk;\nimport com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.chunk.ChunkType1_9_1;\nimport com.viaversion.viaversion.api.type.types.chunk.ChunkType1_9_3;\nimport com.viaversion.viaversion.protocols.v1_8to1_9.packet.ClientboundPackets1_9;\nimport com.viaversion.viaversion.protocols.v1_8to1_9.packet.ServerboundPackets1_9;\nimport com.viaversion.viaversion.protocols.v1_9_1to1_9_3.packet.ClientboundPackets1_9_3;\nimport com.viaversion.viaversion.protocols.v1_9_1to1_9_3.packet.ServerboundPackets1_9_3;\nimport com.viaversion.viaversion.rewriter.text.ComponentRewriterBase;\n\npublic class Protocol1_9_3To1_9_1 extends BackwardsProtocol<ClientboundPackets1_9_3, ClientboundPackets1_9, ServerboundPackets1_9_3, ServerboundPackets1_9> {\n\n    public Protocol1_9_3To1_9_1() {\n        super(ClientboundPackets1_9_3.class, ClientboundPackets1_9.class, ServerboundPackets1_9_3.class, ServerboundPackets1_9.class);\n    }\n\n    @Override\n    protected void registerPackets() {\n        registerClientbound(ClientboundPackets1_9_3.BLOCK_ENTITY_DATA, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.BLOCK_POSITION1_8); //Position\n                map(Types.UNSIGNED_BYTE); //Type\n                map(Types.NAMED_COMPOUND_TAG); //NBT\n                handler(wrapper -> {\n                    if (wrapper.get(Types.UNSIGNED_BYTE, 0) == 9) {\n                        BlockPosition position = wrapper.get(Types.BLOCK_POSITION1_8, 0);\n                        CompoundTag tag = wrapper.get(Types.NAMED_COMPOUND_TAG, 0);\n\n                        wrapper.clearPacket(); //Clear the packet\n\n                        wrapper.setPacketType(ClientboundPackets1_9.UPDATE_SIGN);\n                        wrapper.write(Types.BLOCK_POSITION1_8, position); // Position\n                        for (int i = 0; i < 4; i++) {\n                            // Should technically be written as COMPONENT, but left as String for simplification/to remove redundant wrapping for VR\n                            StringTag textTag = tag.getStringTag(\"Text\" + (i + 1));\n                            String line = textTag != null ? textTag.getValue() : \"\";\n                            wrapper.write(Types.STRING, line); // Sign line\n                        }\n                    }\n                });\n            }\n        });\n\n        registerClientbound(ClientboundPackets1_9_3.LEVEL_CHUNK, wrapper -> {\n            ClientWorld clientWorld = wrapper.user().getClientWorld(Protocol1_9_3To1_9_1.class);\n\n            ChunkType1_9_3 newType = ChunkType1_9_3.forEnvironment(clientWorld.getEnvironment());\n            ChunkType1_9_1 oldType = ChunkType1_9_1.forEnvironment(clientWorld.getEnvironment()); // Get the old type to not write Block Entities\n\n            Chunk chunk = wrapper.read(newType);\n            wrapper.write(oldType, chunk);\n            BlockEntity1_9_1.handle(chunk.getBlockEntities(), wrapper.user());\n        });\n\n        registerClientbound(ClientboundPackets1_9_3.LOGIN, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.INT); // 0 - Entity ID\n                map(Types.UNSIGNED_BYTE); // 1 - Gamemode\n                map(Types.INT); // 2 - Dimension\n\n                handler(wrapper -> {\n                    ClientWorld clientWorld = wrapper.user().getClientWorld(Protocol1_9_3To1_9_1.class);\n\n                    int dimensionId = wrapper.get(Types.INT, 1);\n                    clientWorld.setEnvironment(dimensionId);\n                });\n            }\n        });\n\n        registerClientbound(ClientboundPackets1_9_3.RESPAWN, new PacketHandlers() {\n            @Override\n            public void register() {\n                map(Types.INT); // 0 - Dimension ID\n\n                handler(wrapper -> {\n                    ClientWorld clientWorld = wrapper.user().getClientWorld(Protocol1_9_3To1_9_1.class);\n\n                    int dimensionId = wrapper.get(Types.INT, 0);\n                    clientWorld.setEnvironment(dimensionId);\n                });\n            }\n        });\n\n        JsonNBTComponentRewriter<ClientboundPackets1_9_3> componentRewriter = new JsonNBTComponentRewriter<>(this, ComponentRewriterBase.ReadType.JSON);\n        componentRewriter.registerComponentPacket(ClientboundPackets1_9_3.CHAT);\n    }\n\n    @Override\n    public void init(UserConnection userConnection) {\n        userConnection.addClientWorld(this.getClass(), new ClientWorld());\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v1_9_3to1_9_1/data/BlockEntity1_9_1.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v1_9_3to1_9_1.data;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.StringTag;\nimport com.viaversion.viabackwards.protocol.v1_9_3to1_9_1.Protocol1_9_3To1_9_1;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.BlockPosition;\nimport com.viaversion.viaversion.api.protocol.packet.PacketWrapper;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.protocols.v1_9_1to1_9_3.packet.ClientboundPackets1_9_3;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\npublic class BlockEntity1_9_1 {\n    private static final Map<String, Integer> TYPES = new HashMap<>();\n\n    static {\n        TYPES.put(\"MobSpawner\", 1);\n        TYPES.put(\"Control\", 2);\n        TYPES.put(\"Beacon\", 3);\n        TYPES.put(\"Skull\", 4);\n        TYPES.put(\"FlowerPot\", 5);\n        TYPES.put(\"Banner\", 6);\n        TYPES.put(\"UNKNOWN\", 7);\n        TYPES.put(\"EndGateway\", 8);\n        TYPES.put(\"Sign\", 9);\n    }\n\n    public static void handle(List<CompoundTag> tags, UserConnection connection) {\n        for (CompoundTag tag : tags) {\n            StringTag idTag = tag.getStringTag(\"id\");\n            if (idTag == null) {\n                continue;\n            }\n\n            String id = idTag.getValue();\n            if (!TYPES.containsKey(id)) {\n                continue;\n            }\n\n            int newId = TYPES.get(id);\n            if (newId == -1) {\n                continue;\n            }\n\n            int x = tag.getNumberTag(\"x\").asInt();\n            short y = tag.getNumberTag(\"y\").asShort();\n            int z = tag.getNumberTag(\"z\").asInt();\n\n            BlockPosition pos = new BlockPosition(x, y, z);\n\n            updateBlockEntity(pos, (short) newId, tag, connection);\n        }\n    }\n\n    private static void updateBlockEntity(BlockPosition pos, short id, CompoundTag tag, UserConnection connection) {\n        PacketWrapper wrapper = PacketWrapper.create(ClientboundPackets1_9_3.BLOCK_ENTITY_DATA, null, connection);\n        wrapper.write(Types.BLOCK_POSITION1_8, pos);\n        wrapper.write(Types.UNSIGNED_BYTE, id);\n        wrapper.write(Types.NAMED_COMPOUND_TAG, tag);\n        wrapper.scheduleSend(Protocol1_9_3To1_9_1.class, false);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v26_1to1_21_11/Protocol26_1To1_21_11.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2025 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v26_1to1_21_11;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.StringTag;\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viabackwards.api.data.BackwardsMappingData;\nimport com.viaversion.viabackwards.api.rewriters.BackwardsRegistryRewriter;\nimport com.viaversion.viabackwards.api.rewriters.text.NBTComponentRewriter;\nimport com.viaversion.viabackwards.protocol.v26_1to1_21_11.rewriter.BlockItemPacketRewriter26_1;\nimport com.viaversion.viabackwards.protocol.v26_1to1_21_11.rewriter.ComponentRewriter26_1;\nimport com.viaversion.viabackwards.protocol.v26_1to1_21_11.rewriter.EntityPacketRewriter26_1;\nimport com.viaversion.viabackwards.protocol.v26_1to1_21_11.storage.DayTimeStorage;\nimport com.viaversion.viabackwards.protocol.v26_1to1_21_11.storage.GameModeStorage;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.RegistryType;\nimport com.viaversion.viaversion.api.minecraft.data.version.StructuredDataKeys1_21_11;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_21_11;\nimport com.viaversion.viaversion.api.minecraft.entitydata.types.EntityDataTypes1_21_11;\nimport com.viaversion.viaversion.api.minecraft.entitydata.types.EntityDataTypes26_1;\nimport com.viaversion.viaversion.api.protocol.packet.provider.PacketTypesProvider;\nimport com.viaversion.viaversion.api.protocol.packet.provider.SimplePacketTypesProvider;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.chunk.ChunkType1_21_5;\nimport com.viaversion.viaversion.api.type.types.chunk.ChunkType26_1;\nimport com.viaversion.viaversion.api.type.types.version.Types1_20_5;\nimport com.viaversion.viaversion.api.type.types.version.Types26_1;\nimport com.viaversion.viaversion.api.type.types.version.VersionedTypes;\nimport com.viaversion.viaversion.data.entity.EntityTrackerBase;\nimport com.viaversion.viaversion.data.item.ItemHasherBase;\nimport com.viaversion.viaversion.protocols.v1_21_11to26_1.Protocol1_21_11To26_1;\nimport com.viaversion.viaversion.protocols.v1_21_11to26_1.packet.ClientboundPacket26_1;\nimport com.viaversion.viaversion.protocols.v1_21_11to26_1.packet.ClientboundPackets26_1;\nimport com.viaversion.viaversion.protocols.v1_21_11to26_1.packet.ServerboundPacket26_1;\nimport com.viaversion.viaversion.protocols.v1_21_11to26_1.packet.ServerboundPackets26_1;\nimport com.viaversion.viaversion.protocols.v1_21_4to1_21_5.rewriter.RecipeDisplayRewriter1_21_5;\nimport com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ServerboundPackets1_21_6;\nimport com.viaversion.viaversion.protocols.v1_21_7to1_21_9.packet.ClientboundConfigurationPackets1_21_9;\nimport com.viaversion.viaversion.protocols.v1_21_7to1_21_9.packet.ServerboundConfigurationPackets1_21_9;\nimport com.viaversion.viaversion.protocols.v1_21_7to1_21_9.packet.ServerboundPacket1_21_9;\nimport com.viaversion.viaversion.protocols.v1_21_9to1_21_11.packet.ClientboundPacket1_21_11;\nimport com.viaversion.viaversion.protocols.v1_21_9to1_21_11.packet.ClientboundPackets1_21_11;\nimport com.viaversion.viaversion.rewriter.BlockRewriter;\nimport com.viaversion.viaversion.rewriter.ParticleRewriter;\nimport com.viaversion.viaversion.rewriter.RecipeDisplayRewriter;\nimport com.viaversion.viaversion.rewriter.TagRewriter;\nimport com.viaversion.viaversion.rewriter.block.BlockRewriter1_21_5;\nimport com.viaversion.viaversion.util.Key;\n\nimport static com.viaversion.viaversion.util.ProtocolUtil.packetTypeMap;\nimport static com.viaversion.viaversion.util.TagUtil.removeNamespaced;\n\npublic final class Protocol26_1To1_21_11 extends BackwardsProtocol<ClientboundPacket26_1, ClientboundPacket1_21_11, ServerboundPacket26_1, ServerboundPacket1_21_9> {\n\n    public static final BackwardsMappingData MAPPINGS = new BackwardsMappingData(\"26.1\", \"1.21.11\", Protocol1_21_11To26_1.class);\n    private final EntityPacketRewriter26_1 entityRewriter = new EntityPacketRewriter26_1(this);\n    private final BlockItemPacketRewriter26_1 itemRewriter = new BlockItemPacketRewriter26_1(this);\n    private final ParticleRewriter<ClientboundPacket26_1> particleRewriter = new ParticleRewriter<>(this);\n    private final NBTComponentRewriter<ClientboundPacket26_1> translatableRewriter = new ComponentRewriter26_1(this);\n    private final TagRewriter<ClientboundPacket26_1> tagRewriter = new TagRewriter<>(this);\n    private final BackwardsRegistryRewriter registryDataRewriter = new BackwardsRegistryRewriter(this);\n    private final BlockRewriter<ClientboundPacket26_1> blockRewriter = new BlockRewriter1_21_5<>(this, ChunkType26_1::new, ChunkType1_21_5::new);\n    private final RecipeDisplayRewriter<ClientboundPacket26_1> recipeRewriter = new RecipeDisplayRewriter1_21_5<>(this);\n\n    public Protocol26_1To1_21_11() {\n        super(ClientboundPacket26_1.class, ClientboundPacket1_21_11.class, ServerboundPacket26_1.class, ServerboundPacket1_21_9.class);\n    }\n\n    @Override\n    protected void registerPackets() {\n        super.registerPackets();\n\n        // Remove new environment attributes\n        registryDataRewriter.addHandler(\"dimension_type\", (key, tag) -> {\n            final CompoundTag attributes = tag.getCompoundTag(\"attributes\");\n            if (attributes != null) {\n                removeNamespaced(attributes, \"visual/block_light_tint\");\n                removeNamespaced(attributes, \"visual/night_vision_color\");\n                removeNamespaced(attributes, \"visual/ambient_light_color\");\n            }\n        });\n\n        // Move around entity variant names and sounds\n        registryDataRewriter.addHandler(\"wolf_sound_variant\", (key, tag) -> {\n            final CompoundTag sounds = tag.getCompoundTag(\"adult_sounds\");\n            tag.remove(\"baby_sounds\");\n            tag.putAll(sounds);\n        });\n        registryDataRewriter.addHandler(\"frog_variant\", (key, tag) -> swapEntityNameAffix(\"frog\", tag));\n        registryDataRewriter.addHandler(\"chicken_variant\", (key, tag) -> swapEntityNameAffix(\"chicken\", tag));\n        registryDataRewriter.addHandler(\"cow_variant\", (key, tag) -> swapEntityNameAffix(\"cow\", tag));\n        registryDataRewriter.addHandler(\"pig_variant\", (key, tag) -> swapEntityNameAffix(\"pig\", tag));\n        registryDataRewriter.addHandler(\"cat_variant\", (key, tag) -> removeEntityNamePrefix(\"cat\", tag));\n        registryDataRewriter.remove(\"world_clock\");\n        registryDataRewriter.remove(\"cat_sound_variant\");\n        registryDataRewriter.remove(\"cow_sound_variant\");\n        registryDataRewriter.remove(\"pig_sound_variant\");\n        registryDataRewriter.remove(\"chicken_sound_variant\");\n\n        tagRewriter.addEmptyTags(RegistryType.BLOCK, \"big_dripleaf_placeable\", \"small_dripleaf_placeable\", \"mushroom_grow_block\", \"bamboo_plantable_on\");\n\n        cancelClientbound(ClientboundPackets26_1.LOW_DISK_SPACE_WARNING);\n        cancelClientbound(ClientboundPackets26_1.GAME_RULE_VALUES);\n\n        registerClientbound(ClientboundPackets26_1.SET_TIME, wrapper -> {\n            final long gameTime = wrapper.passthrough(Types.LONG);\n\n            Long dayTime = null;\n            boolean advanceTime = true;\n\n            final int count = wrapper.read(Types.VAR_INT);\n            for (int i = 0; i < count; i++) {\n                final int clockType = wrapper.read(Types.VAR_INT);\n                final long totalTicks = wrapper.read(Types.VAR_LONG);\n                wrapper.read(Types.FLOAT); // Partial tick\n                final float tickRate = wrapper.read(Types.FLOAT);\n                if (Key.equals(registryDataRewriter.getMappings(\"world_clock\").idToKey(clockType), \"overworld\")) {\n                    dayTime = totalTicks;\n                    advanceTime = tickRate != 0;\n                }\n            }\n\n            final DayTimeStorage dayTimeStorage = wrapper.user().get(DayTimeStorage.class);\n            if (dayTime == null) {\n                // Determine from previously sent values based on the current game time\n                dayTime = dayTimeStorage.setGameTimeAndUpdateDayTime(gameTime);\n                advanceTime = dayTimeStorage.advanceTime();\n            } else {\n                dayTimeStorage.setGameTime(gameTime);\n                dayTimeStorage.setDayTime(dayTime);\n                dayTimeStorage.setAdvanceTime(advanceTime);\n            }\n\n            wrapper.write(Types.LONG, dayTime);\n            wrapper.write(Types.BOOLEAN, advanceTime);\n        });\n    }\n\n    private void removeEntityNamePrefix(final String key, final CompoundTag tag) {\n        final StringTag assetIdTag = tag.getStringTag(\"asset_id\");\n        final String assetId = assetIdTag.getValue();\n        assetIdTag.setValue(assetId.replace(key + \"_\", \"\"));\n    }\n\n    private void swapEntityNameAffix(final String key, final CompoundTag tag) {\n        final StringTag assetIdTag = tag.getStringTag(\"asset_id\");\n        final String assetId = assetIdTag.getValue();\n        if (assetId.contains(key + \"_\")) {\n            assetIdTag.setValue(assetId.replace(key + \"_\", \"\") + \"_\" + key);\n        }\n    }\n\n    @Override\n    public void init(final UserConnection connection) {\n        addEntityTracker(connection, new EntityTrackerBase(connection, EntityTypes1_21_11.PLAYER));\n        addItemHasher(connection, new ItemHasherBase(this, connection));\n        connection.put(new DayTimeStorage());\n        connection.put(new GameModeStorage());\n    }\n\n    @Override\n    public BackwardsMappingData getMappingData() {\n        return MAPPINGS;\n    }\n\n    @Override\n    public EntityPacketRewriter26_1 getEntityRewriter() {\n        return entityRewriter;\n    }\n\n    @Override\n    public BlockItemPacketRewriter26_1 getItemRewriter() {\n        return itemRewriter;\n    }\n\n    @Override\n    public BlockRewriter<ClientboundPacket26_1> getBlockRewriter() {\n        return blockRewriter;\n    }\n\n    @Override\n    public BackwardsRegistryRewriter getRegistryDataRewriter() {\n        return registryDataRewriter;\n    }\n\n    @Override\n    public RecipeDisplayRewriter<ClientboundPacket26_1> getRecipeRewriter() {\n        return recipeRewriter;\n    }\n\n    @Override\n    public ParticleRewriter<ClientboundPacket26_1> getParticleRewriter() {\n        return particleRewriter;\n    }\n\n    @Override\n    public NBTComponentRewriter<ClientboundPacket26_1> getComponentRewriter() {\n        return translatableRewriter;\n    }\n\n    @Override\n    public TagRewriter<ClientboundPacket26_1> getTagRewriter() {\n        return tagRewriter;\n    }\n\n    @Override\n    public Types26_1<StructuredDataKeys1_21_11, EntityDataTypes26_1> types() {\n        return VersionedTypes.V26_1;\n    }\n\n    @Override\n    public Types1_20_5<StructuredDataKeys1_21_11, EntityDataTypes1_21_11> mappedTypes() {\n        return VersionedTypes.V1_21_11;\n    }\n\n    @Override\n    protected PacketTypesProvider<ClientboundPacket26_1, ClientboundPacket1_21_11, ServerboundPacket26_1, ServerboundPacket1_21_9> createPacketTypesProvider() {\n        return new SimplePacketTypesProvider<>(\n            packetTypeMap(unmappedClientboundPacketType, ClientboundPackets26_1.class, ClientboundConfigurationPackets1_21_9.class),\n            packetTypeMap(mappedClientboundPacketType, ClientboundPackets1_21_11.class, ClientboundConfigurationPackets1_21_9.class),\n            packetTypeMap(mappedServerboundPacketType, ServerboundPackets26_1.class, ServerboundConfigurationPackets1_21_9.class),\n            packetTypeMap(unmappedServerboundPacketType, ServerboundPackets1_21_6.class, ServerboundConfigurationPackets1_21_9.class)\n        );\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v26_1to1_21_11/rewriter/BlockItemPacketRewriter26_1.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2025 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v26_1to1_21_11.rewriter;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.viabackwards.api.rewriters.BackwardsStructuredItemRewriter;\nimport com.viaversion.viabackwards.protocol.v26_1to1_21_11.Protocol26_1To1_21_11;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.data.StructuredDataContainer;\nimport com.viaversion.viaversion.api.minecraft.data.StructuredDataKey;\nimport com.viaversion.viaversion.api.minecraft.item.Item;\nimport com.viaversion.viaversion.protocols.v1_21_11to26_1.packet.ClientboundPacket26_1;\nimport com.viaversion.viaversion.protocols.v1_21_7to1_21_9.packet.ServerboundPacket1_21_9;\n\nimport static com.viaversion.viaversion.protocols.v1_21_11to26_1.rewriter.BlockItemPacketRewriter26_1.downgradeData;\nimport static com.viaversion.viaversion.protocols.v1_21_11to26_1.rewriter.BlockItemPacketRewriter26_1.upgradeData;\n\npublic final class BlockItemPacketRewriter26_1 extends BackwardsStructuredItemRewriter<ClientboundPacket26_1, ServerboundPacket1_21_9, Protocol26_1To1_21_11> {\n\n    public BlockItemPacketRewriter26_1(final Protocol26_1To1_21_11 protocol) {\n        super(protocol);\n    }\n\n    @Override\n    protected void handleItemDataComponentsToClient(final UserConnection connection, final Item item, final StructuredDataContainer container) {\n        super.handleItemDataComponentsToClient(connection, item, container);\n        downgradeData(protocol.mappedTypes().structuredDataKeys(), container);\n    }\n\n    @Override\n    protected void handleItemDataComponentsToServer(final UserConnection connection, final Item item, final StructuredDataContainer container) {\n        super.handleItemDataComponentsToServer(connection, item, container);\n        upgradeData(protocol, protocol.types().structuredDataKeys(), container);\n    }\n\n    @Override\n    protected void restoreBackupData(final Item item, final StructuredDataContainer container, final CompoundTag customData) {\n        super.restoreBackupData(item, container, customData);\n        if (!(customData.remove(nbtTagName(\"backup\")) instanceof final CompoundTag backupTag)) {\n            return;\n        }\n\n        restoreIntData(StructuredDataKey.ADDITIONAL_TRADE_COST, container, backupTag);\n        restoreIntData(StructuredDataKey.DYE, container, backupTag);\n        restoreIntData(StructuredDataKey.CAT_SOUND_VARIANT, container, backupTag);\n        restoreIntData(StructuredDataKey.CHICKEN_SOUND_VARIANT, container, backupTag);\n        restoreIntData(StructuredDataKey.COW_SOUND_VARIANT, container, backupTag);\n        restoreIntData(StructuredDataKey.PIG_SOUND_VARIANT, container, backupTag);\n    }\n\n    @Override\n    protected void backupInconvertibleData(final UserConnection connection, final Item item, final StructuredDataContainer dataContainer, final CompoundTag backupTag) {\n        super.backupInconvertibleData(connection, item, dataContainer, backupTag);\n        saveIntData(StructuredDataKey.ADDITIONAL_TRADE_COST, dataContainer, backupTag);\n        saveIntData(StructuredDataKey.DYE, dataContainer, backupTag);\n        saveIntData(StructuredDataKey.CAT_SOUND_VARIANT, dataContainer, backupTag);\n        saveIntData(StructuredDataKey.CHICKEN_SOUND_VARIANT, dataContainer, backupTag);\n        saveIntData(StructuredDataKey.COW_SOUND_VARIANT, dataContainer, backupTag);\n        saveIntData(StructuredDataKey.PIG_SOUND_VARIANT, dataContainer, backupTag);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v26_1to1_21_11/rewriter/ComponentRewriter26_1.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2025 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v26_1to1_21_11.rewriter;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.viabackwards.api.BackwardsProtocol;\nimport com.viaversion.viabackwards.api.rewriters.text.NBTComponentRewriter;\nimport com.viaversion.viaversion.api.connection.UserConnection;\nimport com.viaversion.viaversion.api.minecraft.data.StructuredDataKey;\nimport com.viaversion.viaversion.protocols.v1_21_11to26_1.packet.ClientboundPacket26_1;\n\npublic final class ComponentRewriter26_1 extends NBTComponentRewriter<ClientboundPacket26_1> {\n\n    public ComponentRewriter26_1(final BackwardsProtocol<ClientboundPacket26_1, ?, ?, ?> protocol) {\n        super(protocol);\n    }\n\n    @Override\n    protected void handleShowItem(final UserConnection connection, final CompoundTag itemTag, final CompoundTag componentsTag) {\n        super.handleShowItem(connection, itemTag, componentsTag);\n        if (componentsTag == null) {\n            return;\n        }\n\n        removeDataComponents(componentsTag, StructuredDataKey.ADDITIONAL_TRADE_COST, StructuredDataKey.DAMAGE_RESISTANT26_1,\n            StructuredDataKey.BLOCKS_ATTACKS26_1, StructuredDataKey.PROVIDES_BANNER_PATTERNS26_1, StructuredDataKey.DYE,\n            StructuredDataKey.CAT_SOUND_VARIANT, StructuredDataKey.CHICKEN_SOUND_VARIANT, StructuredDataKey.COW_SOUND_VARIANT, StructuredDataKey.PIG_SOUND_VARIANT);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v26_1to1_21_11/rewriter/EntityPacketRewriter26_1.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2025 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v26_1to1_21_11.rewriter;\n\nimport com.viaversion.viabackwards.api.rewriters.EntityRewriter;\nimport com.viaversion.viabackwards.protocol.v26_1to1_21_11.Protocol26_1To1_21_11;\nimport com.viaversion.viabackwards.protocol.v26_1to1_21_11.storage.GameModeStorage;\nimport com.viaversion.viaversion.api.minecraft.GameMode;\nimport com.viaversion.viaversion.api.minecraft.Vector3d;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityType;\nimport com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_21_11;\nimport com.viaversion.viaversion.api.minecraft.entitydata.types.EntityDataTypes1_21_11;\nimport com.viaversion.viaversion.api.minecraft.entitydata.types.EntityDataTypes26_1;\nimport com.viaversion.viaversion.api.type.Types;\nimport com.viaversion.viaversion.api.type.types.version.VersionedTypes;\nimport com.viaversion.viaversion.protocols.v1_21_11to26_1.packet.ClientboundPacket26_1;\nimport com.viaversion.viaversion.protocols.v1_21_11to26_1.packet.ClientboundPackets26_1;\nimport com.viaversion.viaversion.protocols.v1_21_11to26_1.packet.ServerboundPackets26_1;\nimport com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ServerboundPackets1_21_6;\n\npublic final class EntityPacketRewriter26_1 extends EntityRewriter<ClientboundPacket26_1, Protocol26_1To1_21_11> {\n\n    private static final int INTERACT_ACTION = 0;\n    private static final int ATTACK_ACTION = 1;\n    private static final int INTERACT_AT_ACTION = 2;\n\n    public EntityPacketRewriter26_1(final Protocol26_1To1_21_11 protocol) {\n        super(protocol, VersionedTypes.V1_21_11.entityDataTypes.optionalComponentType, VersionedTypes.V1_21_11.entityDataTypes.booleanType);\n    }\n\n    @Override\n    public void registerPackets() {\n        protocol.appendClientbound(ClientboundPackets26_1.RESPAWN, wrapper -> {\n            final byte gamemode = wrapper.get(Types.BYTE, 0);\n            wrapper.user().get(GameModeStorage.class).setGameMode(gamemode);\n        });\n        protocol.appendClientbound(ClientboundPackets26_1.LOGIN, wrapper -> {\n            final byte gamemode = wrapper.get(Types.BYTE, 0);\n            wrapper.user().get(GameModeStorage.class).setGameMode(gamemode);\n        });\n\n        protocol.registerServerbound(ServerboundPackets1_21_6.INTERACT, wrapper -> {\n            wrapper.passthrough(Types.VAR_INT); // Entity ID\n            final int action = wrapper.read(Types.VAR_INT);\n            switch (action) {\n                case INTERACT_ACTION -> wrapper.cancel(); // Drop \"normal\" interacts, as interact_at is always sent by Vanilla clients, and always sent first, with this following after\n                case ATTACK_ACTION -> {\n                    final boolean spectator = wrapper.user().get(GameModeStorage.class).gameMode() == GameMode.SPECTATOR.id();\n                    wrapper.setPacketType(spectator ? ServerboundPackets26_1.SPECTATE_ENTITY : ServerboundPackets26_1.ATTACK);\n                    wrapper.read(Types.BOOLEAN); // Using secondary action\n                }\n                case INTERACT_AT_ACTION -> {\n                    final float x = wrapper.read(Types.FLOAT);\n                    final float y = wrapper.read(Types.FLOAT);\n                    final float z = wrapper.read(Types.FLOAT);\n                    wrapper.passthrough(Types.VAR_INT); // Hand\n                    wrapper.write(Types.LOW_PRECISION_VECTOR, new Vector3d(x, y, z));\n                    // Keep secondary action\n                }\n                default -> throw new IllegalArgumentException(\"Invalid interact action\");\n            }\n        });\n    }\n\n    @Override\n    protected void registerRewrites() {\n        final EntityDataTypes26_1 entityDataTypes = VersionedTypes.V26_1.entityDataTypes;\n        final EntityDataTypes1_21_11 mappedEntityDataTypes = VersionedTypes.V1_21_11.entityDataTypes;\n        dataTypeMapper()\n            .removed(entityDataTypes.catSoundVariant)\n            .removed(entityDataTypes.cowSoundVariant)\n            .removed(entityDataTypes.pigSoundVariant)\n            .removed(entityDataTypes.chickenSoundVariant)\n            .register();\n        registerEntityDataTypeHandler1_20_3(\n            mappedEntityDataTypes.itemType,\n            mappedEntityDataTypes.blockStateType,\n            mappedEntityDataTypes.optionalBlockStateType,\n            mappedEntityDataTypes.particleType,\n            mappedEntityDataTypes.particlesType,\n            mappedEntityDataTypes.componentType,\n            mappedEntityDataTypes.optionalComponentType\n        );\n\n        filter().type(EntityTypes1_21_11.ZOMBIE_VILLAGER).removeIndex(21); // Is villager data finalized\n        filter().type(EntityTypes1_21_11.VILLAGER).removeIndex(20); // Is villager data finalized\n        filter().type(EntityTypes1_21_11.CAT).removeIndex(24); // Sound variant\n        filter().type(EntityTypes1_21_11.CHICKEN).removeIndex(19); // Sound variant\n        filter().type(EntityTypes1_21_11.PIG).removeIndex(20); // Sound variant\n        filter().type(EntityTypes1_21_11.COW).removeIndex(19); // Sound variant\n        filter().type(EntityTypes1_21_11.TADPOLE).removeIndex(17); // Age locked\n        filter().type(EntityTypes1_21_11.ABSTRACT_AGEABLE).removeIndex(17); // Age locked\n    }\n\n    @Override\n    public EntityType typeFromId(final int type) {\n        return EntityTypes1_21_11.getTypeFromId(type);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v26_1to1_21_11/storage/DayTimeStorage.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v26_1to1_21_11.storage;\n\nimport com.viaversion.viaversion.api.connection.StorableObject;\n\npublic final class DayTimeStorage implements StorableObject {\n\n    private long gameTime;\n    private long dayTime;\n    private boolean advanceTime;\n\n    public long gameTime() {\n        return gameTime;\n    }\n\n    public void setGameTime(final long gameTime) {\n        this.gameTime = gameTime;\n    }\n\n    public long setGameTimeAndUpdateDayTime(final long gameTime) {\n        this.dayTime += gameTime - this.gameTime;\n        this.gameTime = gameTime;\n        return dayTime;\n    }\n\n    public long dayTime() {\n        return dayTime;\n    }\n\n    public void setDayTime(final long dayTime) {\n        this.dayTime = dayTime;\n    }\n\n    public boolean advanceTime() {\n        return advanceTime;\n    }\n\n    public void setAdvanceTime(final boolean advanceTime) {\n        this.advanceTime = advanceTime;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/protocol/v26_1to1_21_11/storage/GameModeStorage.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.protocol.v26_1to1_21_11.storage;\n\nimport com.viaversion.viaversion.api.connection.StorableObject;\n\npublic final class GameModeStorage implements StorableObject {\n\n    private int gameMode = -1;\n\n    public int gameMode() {\n        return gameMode;\n    }\n\n    public void setGameMode(final int gameMode) {\n        this.gameMode = gameMode;\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/utils/BackwardsProtocolLogger.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.utils;\n\nimport com.viaversion.viabackwards.ViaBackwards;\nimport com.viaversion.viaversion.api.protocol.Protocol;\nimport com.viaversion.viaversion.util.ProtocolLogger;\n\npublic final class BackwardsProtocolLogger extends ProtocolLogger {\n\n    public BackwardsProtocolLogger(final Class<? extends Protocol> protocol) {\n        super(ViaBackwards.getPlatform().getLogger(), protocol);\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/utils/ChatUtil.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.utils;\n\nimport com.viaversion.nbt.tag.CompoundTag;\nimport com.viaversion.nbt.tag.Tag;\nimport com.viaversion.viaversion.libs.mcstructs.text.Style;\nimport com.viaversion.viaversion.libs.mcstructs.text.TextComponent;\nimport com.viaversion.viaversion.libs.mcstructs.text.TextFormatting;\nimport com.viaversion.viaversion.libs.mcstructs.text.components.TranslationComponent;\nimport com.viaversion.viaversion.libs.mcstructs.text.stringformat.StringFormat;\nimport com.viaversion.viaversion.libs.mcstructs.text.stringformat.handling.ColorHandling;\nimport com.viaversion.viaversion.libs.mcstructs.text.stringformat.handling.DeserializerUnknownHandling;\nimport com.viaversion.viaversion.libs.mcstructs.text.utils.TextUtils;\nimport com.viaversion.viaversion.util.SerializerVersion;\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.function.Consumer;\nimport java.util.regex.Pattern;\n\npublic final class ChatUtil {\n    private static final Pattern UNUSED_COLOR_PATTERN = Pattern.compile(\"(?>(?>§[0-fk-or])*(§r|\\\\Z))|(?>(?>§[0-f])*(§[0-f]))\");\n    private static final Pattern UNUSED_COLOR_PATTERN_PREFIX = Pattern.compile(\"(?>(?>§[0-fk-or])*(§r))|(?>(?>§[0-f])*(§[0-f]))\");\n\n    // 1.21.6 methods mainly for dialog screens\n\n    public static Tag translate(final String key, final Tag... arguments) {\n        // Same as below but converting \"our\" text components to MCStructs components\n        final TextComponent component = new TranslationComponent(key, Arrays.stream(arguments).map(SerializerVersion.V1_21_6::toComponent).toArray());\n        return SerializerVersion.V1_21_6.toTag(component);\n    }\n\n    public static Tag translate(final String key, final Object... arguments) {\n        final TextComponent component = new TranslationComponent(key, arguments);\n        return SerializerVersion.V1_21_6.toTag(component);\n    }\n\n    public static Tag[] split(final Tag tag, final String delimiter) {\n        final TextComponent component = SerializerVersion.V1_21_6.toComponent(tag);\n        return Arrays.stream(TextUtils.split(component, delimiter, false)).map(SerializerVersion.V1_21_6::toTag).toArray(Tag[]::new);\n    }\n\n    public static Tag fixStyle(final Tag tag) {\n        final TextComponent component = SerializerVersion.V1_21_6.toComponent(tag);\n        if (component.getStyle().getColor() == null) {\n            component.getStyle().setFormatting(TextFormatting.WHITE);\n        }\n        component.getStyle().setItalic(false);\n        return SerializerVersion.V1_21_6.toTag(component);\n    }\n\n    public static CompoundTag translate(final String key) {\n        final CompoundTag tag = new CompoundTag();\n        tag.putString(\"translate\", key);\n        tag.putString(\"color\", \"white\");\n        tag.putBoolean(\"italic\", false);\n        return tag;\n    }\n\n    // Sub 1.12 methods\n\n    public static String removeUnusedColor(String legacy, char defaultColor) {\n        return removeUnusedColor(legacy, defaultColor, false);\n    }\n\n    public static String legacyToJsonString(String legacy, String translation, boolean itemData) {\n        return legacyToJsonString(legacy, text -> {\n            text.append(\" \");\n            text.append(new TranslationComponent(translation));\n        }, itemData);\n    }\n\n    public static String legacyToJsonString(String legacy, Consumer<TextComponent> consumer, boolean itemData) {\n        final TextComponent component = StringFormat.vanilla().fromString(legacy, ColorHandling.RESET, DeserializerUnknownHandling.WHITE);\n        consumer.accept(component);\n\n        if (itemData) {\n            component.setParentStyle((new Style()).setItalic(false));\n        }\n        return SerializerVersion.V1_12.toString(component);\n    }\n\n    private static class ChatFormattingState {\n        private final Set<Character> formatting;\n        private final char defaultColor;\n        private char color;\n\n        private ChatFormattingState(char defaultColor) {\n            this(new HashSet<>(), defaultColor, defaultColor);\n        }\n\n        public ChatFormattingState(Set<Character> formatting, char defaultColor, char color) {\n            this.formatting = formatting;\n            this.defaultColor = defaultColor;\n            this.color = color;\n        }\n\n        private void setColor(char newColor) {\n            formatting.clear();\n            color = newColor;\n        }\n\n        public ChatFormattingState copy() {\n            return new ChatFormattingState(new HashSet<>(formatting), defaultColor, color);\n        }\n\n        public void appendTo(StringBuilder builder) {\n            builder.append('§').append(color);\n            for (Character formatCharacter : formatting) {\n                builder.append('§').append(formatCharacter);\n            }\n        }\n\n        @Override\n        public boolean equals(Object o) {\n            if (this == o) {\n                return true;\n            }\n            if (o == null || getClass() != o.getClass()) {\n                return false;\n            }\n            ChatFormattingState that = (ChatFormattingState) o;\n            return defaultColor == that.defaultColor\n                && color == that.color\n                && Objects.equals(formatting, that.formatting);\n        }\n\n        @Override\n        public int hashCode() {\n            return Objects.hash(formatting, defaultColor, color);\n        }\n\n        public void processNextControlChar(char controlChar) {\n            if (controlChar == 'r') {\n                setColor(defaultColor);\n                return;\n            }\n            if (controlChar == 'l' || controlChar == 'm' || controlChar == 'n' || controlChar == 'o') {\n                formatting.add(controlChar);\n                return;\n            }\n            setColor(controlChar);\n        }\n    }\n\n    public static String fromLegacy(String legacy, char defaultColor, int limit) {\n        return fromLegacy(legacy, defaultColor, limit, false);\n    }\n\n    public static String fromLegacyPrefix(String legacy, char defaultColor, int limit) {\n        return fromLegacy(legacy, defaultColor, limit, true);\n    }\n\n    public static String fromLegacy(String legacy, char defaultColor, int limit, boolean isPrefix) {\n        legacy = removeUnusedColor(legacy, defaultColor, isPrefix);\n        if (legacy.length() > limit) legacy = legacy.substring(0, limit);\n        if (legacy.endsWith(\"§\")) legacy = legacy.substring(0, legacy.length() - 1);\n        return legacy;\n    }\n\n    public static String removeUnusedColor(String legacy, char defaultColor, boolean isPrefix) {\n        if (legacy == null) return null;\n        Pattern pattern = isPrefix ? UNUSED_COLOR_PATTERN_PREFIX : UNUSED_COLOR_PATTERN;\n        legacy = pattern.matcher(legacy).replaceAll(\"$1$2\");\n        StringBuilder builder = new StringBuilder();\n        ChatFormattingState builderState = new ChatFormattingState(defaultColor);\n        ChatFormattingState lastState = new ChatFormattingState(defaultColor);\n        for (int i = 0; i < legacy.length(); i++) {\n            char current = legacy.charAt(i);\n            if (current != '§' || i == legacy.length() - 1) {\n                if (!lastState.equals(builderState)) {\n                    lastState.appendTo(builder);\n                    builderState = lastState.copy();\n                }\n                builder.append(current);\n                continue;\n            }\n            current = legacy.charAt(++i);\n            lastState.processNextControlChar(current);\n        }\n        if (isPrefix && !lastState.equals(builderState)) {\n            lastState.appendTo(builder);\n        }\n        return builder.toString();\n    }\n}\n"
  },
  {
    "path": "common/src/main/java/com/viaversion/viabackwards/utils/VelocityUtil.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.utils;\n\npublic class VelocityUtil {\n\n    public static short toLegacyVelocity(double value) {\n        return (short) Math.max(Short.MIN_VALUE, Math.min(Short.MAX_VALUE, (long) (value * 8000)));\n    }\n\n}\n"
  },
  {
    "path": "common/src/main/java-templates/com/viaversion/viabackwards/utils/VersionInfo.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2025 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage com.viaversion.viabackwards.utils;\n\npublic final class VersionInfo {\n\n    public static final String VERSION = \"{{ version }}\";\n    private static final String IMPLEMENTATION_VERSION = \"{{ impl_version }}\";\n\n    public static String getVersion() {\n        return VERSION;\n    }\n\n    public static String getImplementationVersion() {\n        return IMPLEMENTATION_VERSION;\n    }\n}\n"
  },
  {
    "path": "common/src/main/resources/assets/viabackwards/config.yml",
    "content": "# If you need help, you can join our Discord - https://viaversion.com/discord\n#\n# Always shows a mapped mob's original name, and not only when hovering over it with the cursor.\nalways-show-original-mob-name: true\n#\n# Writes name and level of custom enchantments into the item's lore.\n# Set this to false if you see the entries doubled/if the custom-enchant plugin already writes these into the lore manually.\nadd-custom-enchants-into-lore: true\n#\n# Adds the color of a scoreboard team after its prefix for 1.12 clients on 1.13+ servers.\nadd-teamcolor-to-prefix: true\n#\n# Converts the 1.13 face look-at packet for 1.12- players. Requires a bit of extra caching.\nfix-1_13-face-player: false\n#\n# Fixes 1.13 clients and lower not seeing color or formatting in inventory titles by converting them to legacy text.\n# If you have issues with translatable text being displayed wrongly, disable this.\nfix-formatted-inventory-titles: true\n#\n# Sends inventory acknowledgement packets to act as a replacement for ping packets for sub 1.17 clients.\n# This only takes effect for ids in the short range. Useful for anticheat compatibility.\nhandle-pings-as-inv-acknowledgements: false\n#\n# Adds bedrock blocks at y=0 for sub 1.17 clients. This may allow for weird interactions due to sending fake blocks.\nbedrock-at-y-0: false\n#\n# Shows sculk shriekers as crying obsidian for 1.18.2 clients on 1.19+ servers. This fixes collision and block breaking issues.\n# If disabled, the client will see them as end portal frames.\nsculk-shriekers-to-crying-obsidian: true\n#\n# Shows scaffolding as water for 1.13.2 clients on 1.14+ servers. This fixes collision issues.\n# If disabled, the client will see them as hay blocks.\nscaffolding-to-water: false\n#\n# Maps the darkness effect to blindness for 1.18.2 clients on 1.19+ servers.\nmap-darkness-effect: true\n#\n# If enabled, 1.21.3 clients will receive the first float of 1.21.4+ custom model data as int. Disable if you handle this change yourself.\nmap-custom-model-data: true\n#\n# If enabled, 1.19.3 clients will receive display entities as armor stands with custom entity data on 1.19.4+ servers. Note that\n# this does not support all features display entities offer.\nmap-display-entities: true\n#\n# Suppresses warnings of missing emulations for certain features that are not supported (e.g. world height in 1.17+).\nsuppress-emulation-warnings: false\n#\n# If enabled, dialogs will be shown via chest inventories for 1.21.5 clients on 1.21.6+ servers.\ndialogs-via-chests: true\n#\n# Dialog styling. You can also use translation keys here.\ndialog-style:\n  page-navigation-title: \"&9&lPage navigation\"\n  page-navigation-next: \"&9Left click: &6Go to next page\"\n  page-navigation-previous: \"&9Right click: &6Go to previous page\"\n  toggle-value: \"&9Left click: &6Toggle value\"\n  increase-value: \"&9Left click: &6Increase value by %s\"\n  decrease-value: \"&9Right click: &6Decrease value by %s\"\n  value-range: \"&7(Value between &a%s &7and &a%s&7)\"\n  current-value: \"&7Current value: &a%s\"\n  edit-value: \"&9Left click: &6Edit text\"\n  next-option: \"&9Left click: &6Go to next option\"\n  previous-option: \"&9Right click: &6Go to previous option\"\n  set-text: \"&9Left click/close: &6Set text\"\n  close: \"&9Left click: &6Close\"\n#\n# If enabled, the code of conduct will be shown as a dialog for 1.21.7 clients on 1.21.9+ servers.\n# Note that this is not supported for clients below 1.21.6 right now due to missing support for\n# dialogs during the configuration phase.\ncode-of-conduct-as-dialog: true\n#\n# Passes the original vanilla 1.21.4+ item name into custom_model_data strings.\n# This allows client resource packs to restore the appearance of newer items entirely missing from this older version.\n# Disable if your server creates custom items using modern items as their base.\n# Tip: For server custom items, base them on items in the game before 1.21.4 (e.g. saddle) to ensure compatibility.\npass-original-item-name-to-resource-packs: true\n"
  },
  {
    "path": "common/src/main/resources/assets/viabackwards/data/biome-mappings.json",
    "content": "{\n  \"nether_wastes\": \"nether\",\n  \"soul_sand_valley\": \"nether\",\n  \"crimson_forest\": \"nether\",\n  \"warped_forest\": \"nether\",\n  \"basalt_deltas\": \"nether\",\n  \"dripstone_caves\": \"mountains\",\n  \"lush_caves\": \"mountains\",\n  \"meadow\": \"plains\",\n  \"grove\": \"snowy_mountains\",\n  \"snowy_slopes\": \"snowy_mountains\",\n  \"frozen_peaks\": \"snowy_mountains\",\n  \"jagged_peaks\": \"mountains\",\n  \"stony_peaks\": \"mountains\",\n  \"windswept_hills\": \"mountains\",\n  \"snowy_plains\": \"snowy_tundra\",\n  \"sparse_jungle\": \"jungle_edge\",\n  \"stony_shore\": \"stone_shore\",\n  \"old_growth_pine_taiga\": \"giant_spruce_taiga\",\n  \"windswept_forest\": \"wooded_mountains\",\n  \"wooded_badlands\": \"wooded_badlands_plateau\",\n  \"windswept_gravelly_hills\": \"gravelly_mountains\",\n  \"old_growth_birch_forest\": \"tall_birch_forest\",\n  \"old_growth_spruce_taiga\": \"giant_spruce_taiga\",\n  \"windswept_savanna\": \"shattered_savanna\",\n  \"mangrove_swamp\": \"swamp\",\n  \"deep_dark\": \"mountains\",\n  \"cherry_grove\": \"flower_forest\",\n  \"pale_garden\": \"gravelly_mountains\"\n}\n"
  },
  {
    "path": "common/src/main/resources/assets/viabackwards/data/item-mappings-1.10.json",
    "content": "{\n  \"items\": {\n    \"255\": {\n      \"id\": 217,\n      \"name\": \"1.10 Structure Block\"\n    }\n  },\n  \"block-items\": {\n    \"217\": {\n      \"id\": 287,\n      \"name\": \"1.10 Structure Void\"\n    },\n    \"213\": {\n      \"id\": 159,\n      \"data\": 1,\n      \"name\": \"1.10 Magma Block\"\n    },\n    \"214\": {\n      \"id\": 159,\n      \"data\": 14,\n      \"name\": \"1.10 Nether Wart Block\"\n    },\n    \"215\": {\n      \"id\": 112,\n      \"name\": \"1.10 Red Nether Brick\"\n    },\n    \"216\": {\n      \"id\": 155,\n      \"name\": \"1.10 Bone Block\"\n    }\n  }\n}"
  },
  {
    "path": "common/src/main/resources/assets/viabackwards/data/item-mappings-1.11.1.json",
    "content": "{\n  \"items\": {\n    \"452\": {\n      \"id\": 265,\n      \"name\": \"1.11.1 Iron Nugget\"\n    }\n  }\n}"
  },
  {
    "path": "common/src/main/resources/assets/viabackwards/data/item-mappings-1.11.json",
    "content": "{\n  \"items\": {\n    \"449\": {\n      \"id\": 418,\n      \"name\": \"1.11 Totem of Undying\"\n    },\n    \"450\": {\n      \"id\": 433,\n      \"name\": \"1.11 Shulker Shell\"\n    }\n  },\n  \"block-items\": {\n    \"218\": {\n      \"id\": 23,\n      \"data\": -1,\n      \"name\": \"1.11 Observer\"\n    },\n    \"219-234\": {\n      \"id\": 158,\n      \"name\": \"1.11 %color% Shulker Box\"\n    }\n  }\n}"
  },
  {
    "path": "common/src/main/resources/assets/viabackwards/data/item-mappings-1.12.json",
    "content": "{\n  \"items\": {\n    \"453\": {\n      \"id\": 340,\n      \"name\": \"1.12 Knowledge Book\"\n    },\n    \"355\": {\n      \"id\": 355,\n      \"name\": \"1.12 %vb_color% Bed\"\n    }\n  },\n  \"block-items\": {\n    \"251\": {\n      \"id\": 159,\n      \"data\": -1,\n      \"name\": \"1.12 %vb_color% Concrete\"\n    },\n    \"252\": {\n      \"id\": 35,\n      \"data\": -1,\n      \"name\": \"1.12 %vb_color% Concrete Powder\"\n    },\n    \"235\": {\n      \"id\": 159,\n      \"data\": 0,\n      \"name\": \"1.12 White Glazed Terracotta\"\n    },\n    \"236\": {\n      \"id\": 159,\n      \"data\": 1,\n      \"name\": \"1.12 Orange Glazed Terracotta\"\n    },\n    \"237\": {\n      \"id\": 159,\n      \"data\": 2,\n      \"name\": \"1.12 Magenta Glazed Terracotta\"\n    },\n    \"238\": {\n      \"id\": 159,\n      \"data\": 3,\n      \"name\": \"1.12 Light Blue Glazed Terracotta\"\n    },\n    \"239\": {\n      \"id\": 159,\n      \"data\": 4,\n      \"name\": \"1.12 Yellow Glazed Terracotta\"\n    },\n    \"240\": {\n      \"id\": 159,\n      \"data\": 5,\n      \"name\": \"1.12 Lime Glazed Terracotta\"\n    },\n    \"241\": {\n      \"id\": 159,\n      \"data\": 6,\n      \"name\": \"1.12 Pink Glazed Terracotta\"\n    },\n    \"242\": {\n      \"id\": 159,\n      \"data\": 7,\n      \"name\": \"1.12 Gray Glazed Terracotta\"\n    },\n    \"243\": {\n      \"id\": 159,\n      \"data\": 8,\n      \"name\": \"1.12 Light Gray Glazed Terracotta\"\n    },\n    \"244\": {\n      \"id\": 159,\n      \"data\": 9,\n      \"name\": \"1.12 Cyan Glazed Terracotta\"\n    },\n    \"245\": {\n      \"id\": 159,\n      \"data\": 10,\n      \"name\": \"1.12 Purple Glazed Terracotta\"\n    },\n    \"246\": {\n      \"id\": 159,\n      \"data\": 11,\n      \"name\": \"1.12 Blue Glazed Terracotta\"\n    },\n    \"247\": {\n      \"id\": 159,\n      \"data\": 12,\n      \"name\": \"1.12 Brown Glazed Terracotta\"\n    },\n    \"248\": {\n      \"id\": 159,\n      \"data\": 13,\n      \"name\": \"1.12 Green Glazed Terracotta\"\n    },\n    \"249\": {\n      \"id\": 159,\n      \"data\": 14,\n      \"name\": \"1.12 Red Glazed Terracotta\"\n    },\n    \"250\": {\n      \"id\": 159,\n      \"data\": 15,\n      \"name\": \"1.12 Black Glazed Terracotta\"\n    }\n  }\n}\n"
  },
  {
    "path": "common/src/main/resources/assets/viabackwards/data/translation-mappings.json",
    "content": "{\n  \"26.1\": {\n    \"block.minecraft.golden_dandelion\": \"Golden Dandelion\",\n    \"block.minecraft.potted_golden_dandelion\": \"Potted Golden Dandelion\",\n    \"build.tooLow\": \"Minimum height for building is %s\",\n    \"chat_restriction.chat_and_commands_disabled_by_options\": \"Chat is restricted in client settings.\",\n    \"chat_restriction.chat_disabled_by_options\": \"Player chat is restricted in client settings.\",\n    \"chat_restriction.chat_disabled_by_options.action\": \"Go to the Chat Settings screen\",\n    \"chat_restriction.disabled_by_launcher\": \"Chat is restricted by the launcher\",\n    \"chat_restriction.disabled_by_profile\": \"Chat is restricted by profile settings.\",\n    \"chat_restriction.disabled_by_profile.action\": \"Go to your profile settings\",\n    \"chat_screen.commands_not_allowed\": \"Sending commands is not allowed\",\n    \"chat_screen.messages_not_allowed\": \"Sending chat messages is not allowed\",\n    \"chat_screen.restricted\": \"Chat is restricted. Click this message for details.\",\n    \"chat_screen.restricted.narration\": \"Chat is restricted. Go to the Restrictions screen in World Options.\",\n    \"command.trailing_data\": \"Trailing data found: %s\",\n    \"commands.fetchprofile.entity.success\": \"Resolved profile for entity %s: %s\",\n    \"commands.fetchprofile.no_profile\": \"Entity %s has no profile\",\n    \"commands.swing.failed.notliving\": \"No living entities were found to swing\",\n    \"commands.swing.success.multiple\": \"Made %s entities swing their arms\",\n    \"commands.swing.success.single\": \"Made %s swing an arm\",\n    \"commands.time.no_default_clock\": \"There is no default clock in dimension %s\",\n    \"commands.time.no_time_marker_found\": \"Time marker %s does not exist for clock %s\",\n    \"commands.time.pause\": \"Paused clock %s\",\n    \"commands.time.query.absolute\": \"Clock %s is at %s tick(s)\",\n    \"commands.time.query.gametime\": \"The game time is %s tick(s)\",\n    \"commands.time.query.timeline\": \"Timeline %s is at %s tick(s)\",\n    \"commands.time.query.timeline.repetitions\": \"Timeline %s has passed %s repetition(s)\",\n    \"commands.time.resume\": \"Resumed clock %s\",\n    \"commands.time.set.absolute\": \"Set %s to %s tick(s)\",\n    \"commands.time.set.time_marker\": \"Set %s to time marker %s\",\n    \"commands.time.wrong_timeline_for_clock\": \"Timeline %s is not valid for clock %s\",\n    \"editGamerule.inGame.button\": \"Edit Game Rules...\",\n    \"editGamerule.inGame.disabled.tooltip\": \"Updating game rules requires operator permissions.\",\n    \"editGamerule.inGame.discardChanges.message\": \"Are you sure you want to discard your pending game rule changes?\",\n    \"editGamerule.inGame.discardChanges.title\": \"Game rule Changes\",\n    \"editGamerule.inGame.downloadingGamerules\": \"Retrieving game rules...\",\n    \"gamerule.minecraft.universal_anger.description\": \"Angered neutral mobs attack any nearby player, not just the player that angered them. Works best if forgive_dead_players is disabled.\",\n    \"gui.socialInteractions.tooltip.report.chat_disabled_or_blocked\": \"This player can't be reported because chat is disabled or blocked\",\n    \"key.debug.lightmapTexture\": \"Lightmap Texture\",\n    \"options.worldOptions.button\": \"World Options...\",\n    \"restrictions_screen.button\": \"Restrictions...\",\n    \"restrictions_screen.permission.receive_player_messages.allowed\": \"You can receive messages from players\",\n    \"restrictions_screen.permission.receive_player_messages.denied\": \"You can't receive messages from players\",\n    \"restrictions_screen.permission.receive_system_messages.allowed\": \"You can receive system messages from the server\",\n    \"restrictions_screen.permission.receive_system_messages.denied\": \"You can't receive system messages from the server\",\n    \"restrictions_screen.permission.send_commands.allowed\": \"You can send commands\",\n    \"restrictions_screen.permission.send_commands.denied\": \"You can't send commands\",\n    \"restrictions_screen.permission.send_messages.allowed\": \"You can send chat messages\",\n    \"restrictions_screen.permission.send_messages.denied\": \"You can't send chat messages\",\n    \"restrictions_screen.title\": \"Restrictions\",\n    \"selecteWorld.backupRequiredTooltip\": \"Loading the world requires taking a backup first\",\n    \"selectWorld.backupQuestion.file_fixing_required\": \"Create a backup before upgrading this world?\",\n    \"selectWorld.backupWarning.file_fixing_required\": \"This world needs to be upgraded before you can play it. We strongly suggest creating a backup before continuing in case you experience world corruption.\",\n    \"selectWorld.requiresFileFixingTooltip.edit\": \"This world needs to be upgraded to the latest version before you can edit it due to underlying changes to the world format.\",\n    \"selectWorld.requiresFileFixingTooltip.play\": \"This world needs to be upgraded to the latest version before you can play it due to underlying changes to the world format.\",\n    \"selectWorld.requiresFileFixingTooltip.recreate\": \"This world needs to be upgraded to the latest version before you can recreate it due to underlying changes to the world format.\",\n    \"selectWorld.upgrade_and_play\": \"Upgrade and Play\",\n    \"selectWorld.waitingForBackup.message\": \"Depending on the size of your world, this may take a while. Please do not close the game or shut off your device.\",\n    \"selectWorld.waitingForBackup.title\": \"Creating Backup\",\n    \"selectWorld.world_gen_settings_access\": \"Unable to read or access the world gen settings file! %s\",\n    \"subtitles.entity.baby_cat.ambient\": \"Kitten meows\",\n    \"subtitles.entity.baby_cat.beg_for_food\": \"Kitten begs\",\n    \"subtitles.entity.baby_cat.death\": \"Kitten dies\",\n    \"subtitles.entity.baby_cat.eat\": \"Kitten eats\",\n    \"subtitles.entity.baby_cat.hiss\": \"Kitten hisses\",\n    \"subtitles.entity.baby_cat.hurt\": \"Kitten hurts\",\n    \"subtitles.entity.baby_cat.purr\": \"Kitten purrs\",\n    \"subtitles.entity.baby_chicken.ambient\": \"Chick peeps\",\n    \"subtitles.entity.baby_chicken.death\": \"Chick dies\",\n    \"subtitles.entity.baby_chicken.hurts\": \"Chick hurts\",\n    \"subtitles.entity.baby_horse.ambient\": \"Foal neighs\",\n    \"subtitles.entity.baby_horse.angry\": \"Foal neighs\",\n    \"subtitles.entity.baby_horse.breathe\": \"Foal breathes\",\n    \"subtitles.entity.baby_horse.death\": \"Foal dies\",\n    \"subtitles.entity.baby_horse.eat\": \"Foal eats\",\n    \"subtitles.entity.baby_horse.hurt\": \"Foal hurts\",\n    \"subtitles.entity.baby_horse.land\": \"Foal lands\",\n    \"subtitles.entity.baby_pig.ambient\": \"Baby Pig oinks\",\n    \"subtitles.entity.baby_pig.death\": \"Baby Pig dies\",\n    \"subtitles.entity.baby_pig.eat\": \"Baby Pig eats\",\n    \"subtitles.entity.baby_pig.hurt\": \"Baby Pig hurts\",\n    \"subtitles.entity.baby_wolf.ambient\": \"Puppy yips\",\n    \"subtitles.entity.baby_wolf.death\": \"Puppy dies\",\n    \"subtitles.entity.baby_wolf.growl\": \"Puppy growls\",\n    \"subtitles.entity.baby_wolf.hurt\": \"Puppy hurts\",\n    \"subtitles.entity.baby_wolf.pant\": \"Puppy pants\",\n    \"subtitles.entity.baby_wolf.whine\": \"Puppy whines\",\n    \"subtitles.entity.pig.eat\": \"Pig eats\",\n    \"subtitles.item.golden_dandelion.unuse\": \"Golden Dandelion starts aging\",\n    \"subtitles.item.golden_dandelion.use\": \"Golden Dandelion halts aging\",\n    \"test.error.expected_block_present\": \"Expected block %s to be present\",\n    \"test.error.sequence.minimum_tick\": \"Succeeded before expected tick: expected to wait %s\",\n    \"upgradeWorld.aborted.message\": \"This may happen if another program accessed the world during the upgrade.\\nIt may help to restart your computer and try again. If the issue persists, please consider reporting a bug.\",\n    \"upgradeWorld.aborted.title\": \"Failed to upgrade the world\",\n    \"upgradeWorld.canceled.message\": \"Your world remains as it was before.\",\n    \"upgradeWorld.canceled.title\": \"World upgrade canceled\",\n    \"upgradeWorld.done\": \"Upgrading World Completed\",\n    \"upgradeWorld.failed_cleanup.message\": \"The world was successfully upgraded to the current version and is now available in the world list.\\nWe encountered issues when cleaning up outdated data, therefore the name of the world folder has changed to %s.\",\n    \"upgradeWorld.failed_cleanup.title\": \"Successfully upgraded the world\",\n    \"upgradeWorld.info.converted\": \"Upgraded: %s\",\n    \"upgradeWorld.info.file_fix_stage\": \"Stage: %s/%s\",\n    \"upgradeWorld.info.scanning\": \"Scanning files...\",\n    \"upgradeWorld.info.total\": \"Total: %s\",\n    \"upgradeWorld.joinNow\": \"Do you want to join the world now?\",\n    \"upgradeWorld.progress.type.files\": \"Moving files\",\n    \"upgradeWorld.progress.type.legacy_structures\": \"Upgrading legacy structures\",\n    \"upgradeWorld.progress.type.region\": \"Upgrading regions\",\n    \"upgradeWorld.symlink.message\": \"The selected world contains symbolic links.\\nThe required steps to upgrade the world to the current version do not support the use of symbolic links. To upgrade the world, the links need to be resolved first.\",\n    \"upgradeWorld.symlink.title\": \"Can't upgrade the world\",\n    \"upgradeWorld.title\": \"Upgrading World Files\"\n  },\n  \"1.21.11\": {\n    \"advancements.adventure.spear_many_mobs.description\": \"Hit five mobs in the same Charge attack using the Spear\",\n    \"advancements.adventure.spear_many_mobs.title\": \"Mob Kabob\",\n    \"advMode.setCommand.disabled\": \"Command set: %s, but command blocks are still disabled\",\n    \"chat.queue.tooltip\": \"Click to display next message\",\n    \"commands.stopwatch.already_exists\": \"Stopwatch '%s' already exists\",\n    \"commands.stopwatch.create.success\": \"Created Stopwatch '%s'\",\n    \"commands.stopwatch.does_not_exist\": \"Stopwatch '%s' does not exist\",\n    \"commands.stopwatch.query\": \"Stopwatch '%s' has run for %ss\",\n    \"commands.stopwatch.remove.success\": \"Removed Stopwatch '%s'\",\n    \"commands.stopwatch.restart.success\": \"Restarted Stopwatch '%s'\",\n    \"death.attack.spear\": \"%1$s was speared by %2$s\",\n    \"death.attack.spear.item\": \"%1$s was speared by %2$s using %3$s\",\n    \"debug.crash.message.rebindable\": \"%s + %s is held down. This will crash the game unless released.\",\n    \"debug.entry.currently.inOverlay\": \"%s: Currently only in debug overlay\",\n    \"debug.entry.overlay\": \"In Overlay\",\n    \"debug.profiling.start.rebindable\": \"Profiling started for %s seconds. Use %s + %s to stop early\",\n    \"effect.minecraft.breath_of_the_nautilus\": \"Breath of the Nautilus\",\n    \"enchantment.minecraft.lunge\": \"Lunge\",\n    \"entity.minecraft.camel_husk\": \"Camel Husk\",\n    \"entity.minecraft.nautilus\": \"Nautilus\",\n    \"entity.minecraft.parched\": \"Parched\",\n    \"entity.minecraft.zombie_nautilus\": \"Zombie Nautilus\",\n    \"gamerule.minecraft.elytra_movement_check\": \"Do elytra movement check\",\n    \"gamerule.minecraft.fire_spread_radius_around_player\": \"Fire spread radius\",\n    \"gamerule.minecraft.fire_spread_radius_around_player.description\": \"The radius in blocks around a player in which fire can spread\",\n    \"gamerule.minecraft.player_movement_check\": \"Do player movement check\",\n    \"gamerule.minecraft.raids\": \"Do raids\",\n    \"item.intangible\": \"Intangible\",\n    \"item.minecraft.camel_husk_spawn_egg\": \"Camel Husk Spawn Egg\",\n    \"item.minecraft.copper_nautilus_armor\": \"Copper Nautilus Armor\",\n    \"item.minecraft.copper_spear\": \"Copper Spear\",\n    \"item.minecraft.diamond_nautilus_armor\": \"Diamond Nautilus Armor\",\n    \"item.minecraft.diamond_spear\": \"Diamond Spear\",\n    \"item.minecraft.golden_nautilus_armor\": \"Golden Nautilus Armor\",\n    \"item.minecraft.golden_spear\": \"Golden Spear\",\n    \"item.minecraft.iron_nautilus_armor\": \"Iron Nautilus Armor\",\n    \"item.minecraft.iron_spear\": \"Iron Spear\",\n    \"item.minecraft.nautilus_spawn_egg\": \"Nautilus Spawn Egg\",\n    \"item.minecraft.netherite_horse_armor\": \"Netherite Horse Armor\",\n    \"item.minecraft.netherite_nautilus_armor\": \"Netherite Nautilus Armor\",\n    \"item.minecraft.netherite_spear\": \"Netherite Spear\",\n    \"item.minecraft.parched_spawn_egg\": \"Parched Spawn Egg\",\n    \"item.minecraft.stone_spear\": \"Stone Spear\",\n    \"item.minecraft.wooden_spear\": \"Wooden Spear\",\n    \"item.minecraft.zombie_nautilus_spawn_egg\": \"Zombie Nautilus Spawn Egg\",\n    \"key.category.minecraft.debug\": \"Debug\",\n    \"key.debug.clearChat\": \"Clear Chat\",\n    \"key.debug.copyLocation\": \"Copy Location\",\n    \"key.debug.copyRecreateCommand\": \"Copy Data\",\n    \"key.debug.crash\": \"Debug Crash\",\n    \"key.debug.dumpDynamicTextures\": \"Dump Dynamic Textures\",\n    \"key.debug.dumpVersion\": \"Dump Version Info\",\n    \"key.debug.focusPause\": \"Toggle Lost Focus Pause\",\n    \"key.debug.fpsCharts\": \"FPS Charts\",\n    \"key.debug.modifier\": \"Debug Modifier Key\",\n    \"key.debug.networkCharts\": \"Network Charts\",\n    \"key.debug.overlay\": \"Toggle Overlay\",\n    \"key.debug.profiling\": \"Start/Stop Profiling\",\n    \"key.debug.profilingChart\": \"Profiling Chart\",\n    \"key.debug.reloadChunk\": \"Reload Chunks\",\n    \"key.debug.reloadResourcePacks\": \"Reload Resource Packs\",\n    \"key.debug.showAdvancedTooltips\": \"Show Advanced Tooltips\",\n    \"key.debug.showChunkBorders\": \"Show Chunk Boundaries\",\n    \"key.debug.showHitboxes\": \"Show Hitboxes\",\n    \"key.debug.spectate\": \"Cycle Spectator\",\n    \"key.debug.switchGameMode\": \"Game Mode Switcher\",\n    \"key.toggleGui\": \"Toggle GUI\",\n    \"key.toggleSpectatorShaderEffects\": \"Toggle Spectator Shader Effects\",\n    \"mco.backup.entry.worldType.adventureMap\": \"Adventure Map\",\n    \"mco.backup.entry.worldType.experience\": \"Experience\",\n    \"mco.configure.world.name.validation.whitespace\": \"Must not start or end with whitespace. It will be trimmed.\",\n    \"narration.checkbox.usage.focused.check\": \"Press Enter to check\",\n    \"narration.checkbox.usage.focused.uncheck\": \"Press Enter to uncheck\",\n    \"narration.checkbox.usage.hovered.check\": \"Left click to check\",\n    \"narration.checkbox.usage.hovered.uncheck\": \"Left click to uncheck\",\n    \"options.blocks\": \"%s Blocks\",\n    \"options.chunkFade\": \"Chunk Fade Time\",\n    \"options.chunkFade.none\": \"Chunk Fade: None\",\n    \"options.chunkFade.seconds\": \"Chunk Fade: %s second(s)\",\n    \"options.chunkFade.tooltip\": \"How long in seconds chunks should fade in when they're first rendered, if at all.\",\n    \"options.cutoutLeaves\": \"See-Through Leaves\",\n    \"options.cutoutLeaves.tooltip\": \"Allows you to see through gaps in leaves. Disabling improves performance.\",\n    \"options.graphics.preset\": \"Preset\",\n    \"options.graphics.preset.tooltip\": \"Sets \\\"Quality & Performance\\\" settings to reasonable defaults corresponding to the desired quality.\",\n    \"options.improvedTransparency\": \"Improved Transparency\",\n    \"options.improvedTransparency.tooltip\": \"An experimental approach that uses screen shaders for drawing weather, clouds, and particles behind translucent blocks and water.\\nThis will impact GPU performance.\",\n    \"options.maxAnisotropy\": \"Anisotropic Filtering\",\n    \"options.maxAnisotropy.tooltip\": \"Each level significantly improves how smooth textures look, but impacts performance and significantly impacts video memory usage. Requires Texture Filtering to be set to Anisotropic.\",\n    \"options.musicToast\": \"Music Toast\",\n    \"options.musicToast.never.tooltip\": \"No music toast is shown.\",\n    \"options.musicToast.pauseMenu\": \"Pause Menu\",\n    \"options.musicToast.pauseMenu.tooltip\": \"A music toast is constantly displayed in the in-game pause menu while a song is playing.\",\n    \"options.musicToast.pauseMenuAndToast\": \"Pause Menu and Toast\",\n    \"options.musicToast.pauseMenuAndToast.tooltip\": \"Displays a toast when a song starts playing. The same toast is constantly displayed in the in-game pause menu while a song is playing.\",\n    \"options.textureFiltering\": \"Texture Filtering\",\n    \"options.textureFiltering.anisotropic\": \"Anisotropic\",\n    \"options.textureFiltering.anisotropic.tooltip\": \"A hardware based filtering method, but impacts performance and significantly impacts video memory usage. May not be supported on all hardware.\",\n    \"options.textureFiltering.none.tooltip\": \"Textures are displayed without any filtering. Blocks may look blurry when viewed at an angle.\",\n    \"options.textureFiltering.rgss\": \"RGSS\",\n    \"options.textureFiltering.rgss.tooltip\": \"(Rotated Grid Super Sampling)\\nA shader based filtering method that improves texture quality with a moderate performance impact.\",\n    \"options.video.display.header\": \"Display\",\n    \"options.video.interface.header\": \"Interface\",\n    \"options.video.preferences.header\": \"Preferences\",\n    \"options.video.quality.header\": \"Quality & Performance\",\n    \"options.vignette\": \"Show Vignette\",\n    \"options.vignette.tooltip\": \"This is a subtle texture over the game screen used for reducing brightness towards the edges of the screen and warning about the world border.\",\n    \"options.weatherRadius\": \"Weather Effect Radius\",\n    \"options.weatherRadius.tooltip\": \"Radius of the area where rain and snow effects are visible. Very low performance impact.\",\n    \"stat.minecraft.nautilus_one_cm\": \"Distance by Nautilus\",\n    \"subtitles.entity.baby_nautilus.ambient\": \"Baby Nautilus chitters\",\n    \"subtitles.entity.baby_nautilus.ambient_land\": \"Baby Nautilus chitters\",\n    \"subtitles.entity.baby_nautilus.death\": \"Baby Nautilus dies\",\n    \"subtitles.entity.baby_nautilus.death_land\": \"Baby Nautilus dies\",\n    \"subtitles.entity.baby_nautilus.eat\": \"Baby Nautilus eats\",\n    \"subtitles.entity.baby_nautilus.hurt\": \"Baby Nautilus hurts\",\n    \"subtitles.entity.baby_nautilus.hurt_land\": \"Baby Nautilus hurts\",\n    \"subtitles.entity.baby_nautilus.swim\": \"Baby Nautilus swims\",\n    \"subtitles.entity.camel_husk.ambient\": \"Camel Husk grumphs\",\n    \"subtitles.entity.camel_husk.dash\": \"Camel Husk yeets\",\n    \"subtitles.entity.camel_husk.dash_ready\": \"Camel Husk recovers\",\n    \"subtitles.entity.camel_husk.death\": \"Camel Husk dies\",\n    \"subtitles.entity.camel_husk.eat\": \"Camel Husk eats\",\n    \"subtitles.entity.camel_husk.hurt\": \"Camel Husk hurts\",\n    \"subtitles.entity.camel_husk.sit\": \"Camel Husk sits down\",\n    \"subtitles.entity.camel_husk.stand\": \"Camel Husk stands up\",\n    \"subtitles.entity.nautilus.ambient\": \"Nautilus clacks\",\n    \"subtitles.entity.nautilus.ambient_land\": \"Nautilus clacks\",\n    \"subtitles.entity.nautilus.dash\": \"Nautilus jets\",\n    \"subtitles.entity.nautilus.dash_land\": \"Nautilus jets\",\n    \"subtitles.entity.nautilus.dash_ready\": \"Nautilus recovers\",\n    \"subtitles.entity.nautilus.dash_ready_land\": \"Nautilus recovers\",\n    \"subtitles.entity.nautilus.death\": \"Nautilus dies\",\n    \"subtitles.entity.nautilus.death_land\": \"Nautilus dies\",\n    \"subtitles.entity.nautilus.eat\": \"Nautilus eats\",\n    \"subtitles.entity.nautilus.hurt\": \"Nautilus hurts\",\n    \"subtitles.entity.nautilus.hurt_land\": \"Nautilus hurts\",\n    \"subtitles.entity.nautilus.swim\": \"Nautilus swims\",\n    \"subtitles.entity.parched.ambient\": \"Parched crackles\",\n    \"subtitles.entity.parched.death\": \"Parched dies\",\n    \"subtitles.entity.parched.hurt\": \"Parched hurts\",\n    \"subtitles.entity.parrot.imitate.camel_husk\": \"Parrot grumphs\",\n    \"subtitles.entity.parrot.imitate.parched\": \"Parrot crackles\",\n    \"subtitles.entity.parrot.imitate.zombie_nautilus\": \"Parrot gargles\",\n    \"subtitles.entity.zombie_horse.angry\": \"Zombie Horse neighs\",\n    \"subtitles.entity.zombie_horse.eat\": \"Zombie Horse eats\",\n    \"subtitles.entity.zombie_nautilus.ambient\": \"Zombie Nautilus burbles\",\n    \"subtitles.entity.zombie_nautilus.ambient_land\": \"Zombie Nautilus burbles\",\n    \"subtitles.entity.zombie_nautilus.dash\": \"Zombie Nautilus jets\",\n    \"subtitles.entity.zombie_nautilus.dash_land\": \"Zombie Nautilus jets\",\n    \"subtitles.entity.zombie_nautilus.dash_ready\": \"Zombie Nautilus recovers\",\n    \"subtitles.entity.zombie_nautilus.dash_ready_land\": \"Zombie Nautilus recovers\",\n    \"subtitles.entity.zombie_nautilus.death\": \"Zombie Nautilus dies\",\n    \"subtitles.entity.zombie_nautilus.death_land\": \"Zombie Nautilus dies\",\n    \"subtitles.entity.zombie_nautilus.eat\": \"Zombie Nautilus eats\",\n    \"subtitles.entity.zombie_nautilus.hurt\": \"Zombie Nautilus hurts\",\n    \"subtitles.entity.zombie_nautilus.hurt_land\": \"Zombie Nautilus hurts\",\n    \"subtitles.entity.zombie_nautilus.swim\": \"Zombie Nautilus swims\",\n    \"subtitles.item.armor.equip_nautilus\": \"Nautilus Armor equips\",\n    \"subtitles.item.armor.unequip_nautilus\": \"Nautilus Armor unequips\",\n    \"subtitles.item.spear_wood.attack\": \"Spear jabs\",\n    \"subtitles.item.spear_wood.hit\": \"Spear hits\",\n    \"subtitles.item.spear_wood.use\": \"Charges with Spear\",\n    \"subtitles.item.spear.attack\": \"Spear jabs\",\n    \"subtitles.item.spear.hit\": \"Spear hits\",\n    \"subtitles.item.spear.lunge\": \"Spear lunges\",\n    \"subtitles.item.spear.use\": \"Charges with Spear\"\n  },\n  \"1.21.9\": {\n    \"advMode.notEnabled.spawner\": \"Spawner blocks are not enabled\",\n    \"block.minecraft.acacia_shelf\": \"Acacia Shelf\",\n    \"block.minecraft.bamboo_shelf\": \"Bamboo Shelf\",\n    \"block.minecraft.birch_shelf\": \"Birch Shelf\",\n    \"block.minecraft.cherry_shelf\": \"Cherry Shelf\",\n    \"block.minecraft.copper_bars\": \"Copper Bars\",\n    \"block.minecraft.copper_chain\": \"Copper Chain\",\n    \"block.minecraft.copper_chest\": \"Copper Chest\",\n    \"block.minecraft.copper_golem_statue\": \"Copper Golem Statue\",\n    \"block.minecraft.copper_lantern\": \"Copper Lantern\",\n    \"block.minecraft.copper_torch\": \"Copper Torch\",\n    \"block.minecraft.copper_wall_torch\": \"Copper Wall Torch\",\n    \"block.minecraft.crimson_shelf\": \"Crimson Shelf\",\n    \"block.minecraft.dark_oak_shelf\": \"Dark Oak Shelf\",\n    \"block.minecraft.exposed_copper_bars\": \"Exposed Copper Bars\",\n    \"block.minecraft.exposed_copper_chain\": \"Exposed Copper Chain\",\n    \"block.minecraft.exposed_copper_chest\": \"Exposed Copper Chest\",\n    \"block.minecraft.exposed_copper_golem_statue\": \"Exposed Copper Golem Statue\",\n    \"block.minecraft.exposed_copper_lantern\": \"Exposed Copper Lantern\",\n    \"block.minecraft.exposed_lightning_rod\": \"Exposed Lightning Rod\",\n    \"block.minecraft.iron_chain\": \"Iron Chain\",\n    \"block.minecraft.jungle_shelf\": \"Jungle Shelf\",\n    \"block.minecraft.mangrove_shelf\": \"Mangrove Shelf\",\n    \"block.minecraft.oak_shelf\": \"Oak Shelf\",\n    \"block.minecraft.oxidized_copper_bars\": \"Oxidized Copper Bars\",\n    \"block.minecraft.oxidized_copper_chain\": \"Oxidized Copper Chain\",\n    \"block.minecraft.oxidized_copper_chest\": \"Oxidized Copper Chest\",\n    \"block.minecraft.oxidized_copper_golem_statue\": \"Oxidized Copper Golem Statue\",\n    \"block.minecraft.oxidized_copper_lantern\": \"Oxidized Copper Lantern\",\n    \"block.minecraft.oxidized_lightning_rod\": \"Oxidized Lightning Rod\",\n    \"block.minecraft.pale_oak_shelf\": \"Pale Oak Shelf\",\n    \"block.minecraft.spruce_shelf\": \"Spruce Shelf\",\n    \"block.minecraft.warped_shelf\": \"Warped Shelf\",\n    \"block.minecraft.waxed_copper_bars\": \"Waxed Copper Bars\",\n    \"block.minecraft.waxed_copper_chain\": \"Waxed Copper Chain\",\n    \"block.minecraft.waxed_copper_chest\": \"Waxed Copper Chest\",\n    \"block.minecraft.waxed_copper_golem_statue\": \"Waxed Copper Golem Statue\",\n    \"block.minecraft.waxed_copper_lantern\": \"Waxed Copper Lantern\",\n    \"block.minecraft.waxed_exposed_copper_bars\": \"Waxed Exposed Copper Bars\",\n    \"block.minecraft.waxed_exposed_copper_chain\": \"Waxed Exposed Copper Chain\",\n    \"block.minecraft.waxed_exposed_copper_chest\": \"Waxed Exposed Copper Chest\",\n    \"block.minecraft.waxed_exposed_copper_golem_statue\": \"Waxed Exposed Copper Golem Statue\",\n    \"block.minecraft.waxed_exposed_copper_lantern\": \"Waxed Exposed Copper Lantern\",\n    \"block.minecraft.waxed_exposed_lightning_rod\": \"Waxed Exposed Lightning Rod\",\n    \"block.minecraft.waxed_lightning_rod\": \"Waxed Lightning Rod\",\n    \"block.minecraft.waxed_oxidized_copper_bars\": \"Waxed Oxidized Copper Bars\",\n    \"block.minecraft.waxed_oxidized_copper_chain\": \"Waxed Oxidized Copper Chain\",\n    \"block.minecraft.waxed_oxidized_copper_chest\": \"Waxed Oxidized Copper Chest\",\n    \"block.minecraft.waxed_oxidized_copper_golem_statue\": \"Waxed Oxidized Copper Golem Statue\",\n    \"block.minecraft.waxed_oxidized_copper_lantern\": \"Waxed Oxidized Copper Lantern\",\n    \"block.minecraft.waxed_oxidized_lightning_rod\": \"Waxed Oxidized Lightning Rod\",\n    \"block.minecraft.waxed_weathered_copper_bars\": \"Waxed Weathered Copper Bars\",\n    \"block.minecraft.waxed_weathered_copper_chain\": \"Waxed Weathered Copper Chain\",\n    \"block.minecraft.waxed_weathered_copper_chest\": \"Waxed Weathered Copper Chest\",\n    \"block.minecraft.waxed_weathered_copper_golem_statue\": \"Waxed Weathered Copper Golem Statue\",\n    \"block.minecraft.waxed_weathered_copper_lantern\": \"Waxed Weathered Copper Lantern\",\n    \"block.minecraft.waxed_weathered_lightning_rod\": \"Waxed Weathered Lightning Rod\",\n    \"block.minecraft.weathered_copper_bars\": \"Weathered Copper Bars\",\n    \"block.minecraft.weathered_copper_chain\": \"Weathered Copper Chain\",\n    \"block.minecraft.weathered_copper_chest\": \"Weathered Copper Chest\",\n    \"block.minecraft.weathered_copper_golem_statue\": \"Weathered Copper Golem Statue\",\n    \"block.minecraft.weathered_copper_lantern\": \"Weathered Copper Lantern\",\n    \"block.minecraft.weathered_lightning_rod\": \"Weathered Lightning Rod\",\n    \"commands.fetchprofile.copy_component\": \"Copy Component\",\n    \"commands.fetchprofile.copy_text\": \"Copy %s\",\n    \"commands.fetchprofile.failed_to_serialize\": \"Failed to serialize profile: %s\",\n    \"commands.fetchprofile.give_item\": \"Give Item\",\n    \"commands.fetchprofile.id.failure\": \"Failed to resolve profile for ID %s\",\n    \"commands.fetchprofile.id.success\": \"Resolved profile for ID %s: %s\",\n    \"commands.fetchprofile.name.failure\": \"Failed to resolve profile for name %s\",\n    \"commands.fetchprofile.name.success\": \"Resolved profile for name %s: %s\",\n    \"commands.fetchprofile.summon_mannequin\": \"Summon Mannequin\",\n    \"commands.profile_fetch.copy_component\": \"Copy Component\",\n    \"commands.profile_fetch.failed_to_serialize\": \"Failed to serialize profile: %s\",\n    \"commands.profile_fetch.give_item\": \"Give Item\",\n    \"commands.profile_fetch.id.failure\": \"Failed to resolved profile for id %s\",\n    \"commands.profile_fetch.id.success\": \"Resolved profile for id %s: %s\",\n    \"commands.profile_fetch.name.failure\": \"Failed to resolve profile for name %s\",\n    \"commands.profile_fetch.name.success\": \"Resolved profile for name %s: %s\",\n    \"commands.setworldspawn.success.new\": \"Set the world spawn point to %s, %s, %s [%s, %s] in %s\",\n    \"commands.spawnpoint.success.multiple.new\": \"Set spawn point to %s, %s, %s [%s, %s] in %s for %s players\",\n    \"commands.spawnpoint.success.single.new\": \"Set spawn point to %s, %s, %s [%s, %s] in %s for %s\",\n    \"commands.spectate.cannot_spectate\": \"%s cannot be spectated\",\n    \"commands.summon.failed.peaceful\": \"Monsters cannot be summoned in Peaceful difficulty\",\n    \"component.profile.dynamic\": \"Dynamic\",\n    \"debug.entry.currently.alwaysOn\": \"%s: Currently always on\",\n    \"debug.entry.currently.inF3\": \"%s: Currently only in F3\",\n    \"debug.entry.currently.never\": \"%s: Currently off\",\n    \"debug.entry.f3\": \"In F3\",\n    \"debug.options.category.renderer\": \"Debug Renderers\",\n    \"debug.options.category.text\": \"Debug Screen Text\",\n    \"debug.options.help\": \"F3 + F6 = Edit debug options\",\n    \"debug.options.notAllowed.tooltip\": \"Not visible when debug info is reduced\",\n    \"debug.options.profile.default\": \"Default profile\",\n    \"debug.options.profile.performance\": \"Performance profile\",\n    \"debug.options.title\": \"Debug Options\",\n    \"debug.options.warning\": \"These options are for testing purposes only. They may slow down your computer, crash the game, or eat your pet rock.\",\n    \"entity.minecraft.copper_golem\": \"Copper Golem\",\n    \"entity.minecraft.mannequin\": \"Mannequin\",\n    \"entity.minecraft.mannequin.label\": \"NPC\",\n    \"gamerule.allowEnteringNetherUsingPortals\": \"Allow Nether\",\n    \"gamerule.allowEnteringNetherUsingPortals.description\": \"Controls whether players are allowed to enter the Nether.\",\n    \"gamerule.commandBlocksEnabled\": \"Enable Command Blocks\",\n    \"gamerule.enableCommandBlocks\": \"Enable Command Blocks\",\n    \"gamerule.pvp\": \"Enable pvp\",\n    \"gamerule.pvp.description\": \"Controls whether players are allowed to damage other players.\",\n    \"gamerule.spawnerBlocksEnabled\": \"Enable Spawner Blocks\",\n    \"gamerule.spawnMonsters.description\": \"Controls whether monsters naturally spawn.\",\n    \"gui.stats.none_found\": \"No statistics found.\",\n    \"item.minecraft.copper_axe\": \"Copper Axe\",\n    \"item.minecraft.copper_boots\": \"Copper Boots\",\n    \"item.minecraft.copper_chestplate\": \"Copper Chestplate\",\n    \"item.minecraft.copper_golem_spawn_egg\": \"Copper Golem Spawn Egg\",\n    \"item.minecraft.copper_helmet\": \"Copper Helmet\",\n    \"item.minecraft.copper_hoe\": \"Copper Hoe\",\n    \"item.minecraft.copper_horse_armor\": \"Copper Horse Armor\",\n    \"item.minecraft.copper_leggings\": \"Copper Leggings\",\n    \"item.minecraft.copper_nugget\": \"Copper Nugget\",\n    \"item.minecraft.copper_pickaxe\": \"Copper Pickaxe\",\n    \"item.minecraft.copper_shovel\": \"Copper Shovel\",\n    \"item.minecraft.copper_sword\": \"Copper Sword\",\n    \"item.spawn_egg.peaceful\": \"Disabled in Peaceful\",\n    \"key.spectatorHotbar\": \"Select On Hotbar\",\n    \"mco.configure.world.players.invite.duplicate\": \"A player with the provided name has already been invited to the Realm\",\n    \"multiplayer.codeOfConduct.check\": \"Do not notify again for this Code of Conduct\",\n    \"multiplayer.codeOfConduct.title\": \"Server Code of Conduct\",\n    \"multiplayer.confirm_command.run_command\": \"Run Command\",\n    \"multiplayer.confirm_command.signature_required\": \"You are trying to execute a command that will send chat messages using your name.\\nIt can only be run from the chat screen\\nCommand: %s\",\n    \"multiplayer.confirm_command.suggest_command\": \"Copy to Chat Screen\",\n    \"multiplayer.disconnect.banned.reason.default\": \"Banned by an operator.\",\n    \"multiplayer.disconnect.code_of_conduct\": \"Server requires accepting the Code of Conduct\",\n    \"multiplayer.disconnect.configuration_error\": \"Unexpected error during configuration\",\n    \"multiplayer.status.anonymous_player\": \"Anonymous Player\",\n    \"narration.slider.usage.focused.keyboard_cannot_change_value\": \"Press Enter to start changing the slider value\",\n    \"options.allowCursorChanges\": \"Allow Cursor Changes\",\n    \"options.allowCursorChanges.tooltip\": \"Allows the mouse cursor to change shape when over certain UI elements.\",\n    \"options.chat.drafts\": \"Save Unsent Chats\",\n    \"options.chat.drafts.tooltip\": \"Unsent messages will be saved and can be sent the next time chat is opened.\",\n    \"options.invertMouseX\": \"Invert Mouse X\",\n    \"options.invertMouseY\": \"Invert Mouse Y\",\n    \"options.showSubtitles.tooltip\": \"Enables captions for sounds played in the game.\",\n    \"options.sprintWindow\": \"Sprint Window\",\n    \"options.sprintWindow.tooltip\": \"Time window in ticks where double-tapping the forward key activates sprint.\",\n    \"pack.incompatible.confirm.unknown\": \"This pack is broken or made for an unknown version of Minecraft and may not work correctly.\",\n    \"pack.incompatible.unknown\": \"(Broken or incompatible)\",\n    \"subtitles.block.shelf.activate\": \"Shelf activates\",\n    \"subtitles.block.shelf.deactivate\": \"Shelf deactivates\",\n    \"subtitles.block.shelf.multi_swap\": \"Items swap\",\n    \"subtitles.block.shelf.place_item\": \"Item placed\",\n    \"subtitles.block.shelf.single_swap\": \"Item swaps\",\n    \"subtitles.block.shelf.take_item\": \"Item taken\",\n    \"subtitles.entity.copper_golem_become_statue\": \"Copper Golem is petrified\",\n    \"subtitles.entity.copper_golem_oxidized.death\": \"Copper Golem dies\",\n    \"subtitles.entity.copper_golem_oxidized.hurt\": \"Copper Golem hurts\",\n    \"subtitles.entity.copper_golem_oxidized.spin\": \"Copper Golem's head spins\",\n    \"subtitles.entity.copper_golem_weathered.death\": \"Copper Golem dies\",\n    \"subtitles.entity.copper_golem_weathered.hurt\": \"Copper Golem hurts\",\n    \"subtitles.entity.copper_golem_weathered.spin\": \"Copper Golem's head spins\",\n    \"subtitles.entity.copper_golem.death\": \"Copper Golem dies\",\n    \"subtitles.entity.copper_golem.hurt\": \"Copper Golem hurts\",\n    \"subtitles.entity.copper_golem.item_drop\": \"Copper Golem is placing an item\",\n    \"subtitles.entity.copper_golem.item_no_drop\": \"Copper Golem can't place item\",\n    \"subtitles.entity.copper_golem.no_item_get\": \"Copper Golem is picking up item\",\n    \"subtitles.entity.copper_golem.no_item_no_get\": \"Copper Golem can't pick up item\",\n    \"subtitles.entity.copper_golem.spawn\": \"Copper Golem appears\",\n    \"subtitles.entity.copper_golem.spin\": \"Copper Golem's head spins\",\n    \"subtitles.item.armor.equip_copper\": \"Copper armor clonks\",\n    \"subtitles.weather.end_flash\": \"End Flash rumbles\"\n  },\n  \"1.21.7\": {\n    \"item.minecraft.music_disc_lava_chicken.desc\": \"Hyper Potions - Lava Chicken\",\n    \"jukebox_song.minecraft.lava_chicken\": \"Hyper Potions - Lava Chicken\",\n    \"painting.minecraft.dennis.title\": \"Dennis\"\n  },\n  \"1.21.6\": {\n    \"advancements.adventure.heart_transplanter.description\": \"Place a Creaking Heart with the correct alignment between two Pale Oak Log blocks\",\n    \"advancements.adventure.heart_transplanter.title\": \"Heart Transplanter\",\n    \"advancements.husbandry.place_dried_ghast_in_water.description\": \"Place a Dried Ghast block into water\",\n    \"advancements.husbandry.place_dried_ghast_in_water.title\": \"Stay Hydrated!\",\n    \"argument.hexcolor.invalid\": \"Invalid hex color code '%s'\",\n    \"argument.resource_or_id.no_such_element\": \"Can't find element '%s' in registry '%s'\",\n    \"argument.waypoint.invalid\": \"Selected entity is not a waypoint\",\n    \"attribute.name.camera_distance\": \"Camera Distance\",\n    \"attribute.name.waypoint_receive_range\": \"Waypoint Receive Range\",\n    \"attribute.name.waypoint_transmit_range\": \"Waypoint Transmit Range\",\n    \"block.minecraft.dried_ghast\": \"Dried Ghast\",\n    \"book.edit.title\": \"Book Edit Screen\",\n    \"book.sign.title\": \"Book Sign Screen\",\n    \"book.sign.titlebox\": \"Title\",\n    \"book.view.title\": \"Book View Screen\",\n    \"commands.datapack.create.already_exists\": \"Pack with name '%s' already exists\",\n    \"commands.datapack.create.invalid_full_name\": \"Invalid new pack name '%s'\",\n    \"commands.datapack.create.invalid_name\": \"Invalid characters in new pack name '%s'\",\n    \"commands.datapack.create.io_failure\": \"Can't create pack with name '%s', check logs\",\n    \"commands.datapack.create.metadata_encode_failure\": \"Failed to encode metadata for pack with name '%s': %s\",\n    \"commands.datapack.create.success\": \"Created new empty pack with name '%s'\",\n    \"commands.dialog.clear.multiple\": \"Cleared dialog for %s players\",\n    \"commands.dialog.clear.single\": \"Cleared dialog for %s\",\n    \"commands.dialog.show.multiple\": \"Displayed dialog to %s players\",\n    \"commands.dialog.show.single\": \"Displayed dialog to %s\",\n    \"commands.version.build_time\": \"build_time = %s\",\n    \"commands.version.data\": \"data = %s\",\n    \"commands.version.header\": \"Server version info:\",\n    \"commands.version.id\": \"id = %s\",\n    \"commands.version.name\": \"name = %s\",\n    \"commands.version.pack.data\": \"pack_data = %s\",\n    \"commands.version.pack.resource\": \"pack_resource = %s\",\n    \"commands.version.protocol\": \"protocol = %s (%s)\",\n    \"commands.version.series\": \"series = %s\",\n    \"commands.version.stable.no\": \"stable = no\",\n    \"commands.version.stable.yes\": \"stable = yes\",\n    \"commands.waypoint.list.empty\": \"No waypoints in %s\",\n    \"commands.waypoint.list.success\": \"%s waypoint(s) in %s: %s\",\n    \"commands.waypoint.modify.color\": \"Waypoint color is now %s\",\n    \"commands.waypoint.modify.color.reset\": \"Reset waypoint color\",\n    \"commands.waypoint.modify.style\": \"Waypoint style changed\",\n    \"dataPack.locator_bar.description\": \"Show the direction of other players in multiplayer\",\n    \"dataPack.locator_bar.name\": \"Locator Bar\",\n    \"debug.version.header\": \"Client version info:\",\n    \"debug.version.help\": \"F3 + V = Client version info\",\n    \"entity.minecraft.happy_ghast\": \"Happy Ghast\",\n    \"gamerule.locatorBar\": \"Enable player Locator Bar\",\n    \"gamerule.locatorBar.description\": \"When enabled, a bar is shown on the screen to indicate the direction of players.\",\n    \"gui.waitingForResponse.button.inactive\": \"Back (%ss)\",\n    \"gui.waitingForResponse.title\": \"Waiting for Server\",\n    \"item.minecraft.black_harness\": \"Black Harness\",\n    \"item.minecraft.blue_harness\": \"Blue Harness\",\n    \"item.minecraft.brown_harness\": \"Brown Harness\",\n    \"item.minecraft.cyan_harness\": \"Cyan Harness\",\n    \"item.minecraft.gray_harness\": \"Gray Harness\",\n    \"item.minecraft.green_harness\": \"Green Harness\",\n    \"item.minecraft.happy_ghast_spawn_egg\": \"Happy Ghast Spawn Egg\",\n    \"item.minecraft.harness\": \"Harness\",\n    \"item.minecraft.light_blue_harness\": \"Light Blue Harness\",\n    \"item.minecraft.light_gray_harness\": \"Light Gray Harness\",\n    \"item.minecraft.lime_harness\": \"Lime Harness\",\n    \"item.minecraft.magenta_harness\": \"Magenta Harness\",\n    \"item.minecraft.music_disc_tears.desc\": \"Amos Roddy - Tears\",\n    \"item.minecraft.orange_harness\": \"Orange Harness\",\n    \"item.minecraft.pink_harness\": \"Pink Harness\",\n    \"item.minecraft.purple_harness\": \"Purple Harness\",\n    \"item.minecraft.red_harness\": \"Red Harness\",\n    \"item.minecraft.white_harness\": \"White Harness\",\n    \"item.minecraft.yellow_harness\": \"Yellow Harness\",\n    \"jukebox_song.minecraft.tears\": \"Amos Roddy - Tears\",\n    \"key.quickActions\": \"Quick Actions\",\n    \"mco.configure.world.buttons.region_preference\": \"Select Region...\",\n    \"mco.configure.world.close.question.title\": \"Need to make changes without disruption?\",\n    \"mco.configure.world.loading\": \"Loading Realm\",\n    \"mco.configure.world.region_preference\": \"Region Preference\",\n    \"mco.configure.world.region_preference.title\": \"Region Preference Selection\",\n    \"mco.configure.world.settings.expired\": \"You cannot edit settings of an expired Realm\",\n    \"mco.connect.region\": \"Server region: %s\",\n    \"mco.errorMessage.realmsService.configurationError\": \"An unexpected error occurred while editing world options\",\n    \"mco.play.button.realm.closed\": \"Realm is closed\",\n    \"menu.custom_options\": \"Custom Options...\",\n    \"menu.custom_options.title\": \"Custom Options\",\n    \"menu.custom_options.tooltip\": \"Note: Custom options are provided by third-party servers and/or content.\\nHandle with care!\",\n    \"menu.custom_screen_info.button_narration\": \"This is a custom screen. Learn more.\",\n    \"menu.custom_screen_info.contents\": \"The contents of this screen are controlled by third-party servers and maps that are not owned, operated, or supervised by Mojang Studios or Microsoft.\\n\\nHandle with care! Always be careful when following links and never give away your personal information, including login details.\\n\\nIf this screen prevents you from playing, you can also disconnect from the current server by using the button below.\",\n    \"menu.custom_screen_info.disconnect\": \"Custom screen rejected\",\n    \"menu.custom_screen_info.title\": \"Note about custom screens\",\n    \"menu.custom_screen_info.tooltip\": \"This is a custom screen. Click here to learn more.\",\n    \"menu.quick_actions\": \"Quick Actions...\",\n    \"menu.quick_actions.title\": \"Quick Actions\",\n    \"multiplayer.confirm_command.parse_errors\": \"You are trying to execute an unrecognized or invalid command.\\nAre you sure?\\nCommand: %s\",\n    \"multiplayer.confirm_command.permissions_required\": \"You are trying to execute a command that requires elevated permissions.\\nThis might negatively affect your game.\\nAre you sure?\\nCommand: %s\",\n    \"multiplayer.confirm_command.title\": \"Confirm Command Execution\",\n    \"music.game.a_familiar_room\": \"Aaron Cherof - A Familiar Room\",\n    \"music.game.an_ordinary_day\": \"Kumi Tanioka - An Ordinary Day\",\n    \"music.game.ancestry\": \"Lena Raine - Ancestry\",\n    \"music.game.below_and_above\": \"Amos Roddy - Below and Above\",\n    \"music.game.broken_clocks\": \"Amos Roddy - Broken Clocks\",\n    \"music.game.bromeliad\": \"Aaron Cherof - Bromeliad\",\n    \"music.game.clark\": \"C418 - Clark\",\n    \"music.game.comforting_memories\": \"Kumi Tanioka - Comforting Memories\",\n    \"music.game.creative.aria_math\": \"C418 - Aria Math\",\n    \"music.game.creative.biome_fest\": \"C418 - Biome Fest\",\n    \"music.game.creative.blind_spots\": \"C418 - Blind Spots\",\n    \"music.game.creative.dreiton\": \"C418 - Dreiton\",\n    \"music.game.creative.haunt_muskie\": \"C418 - Haunt Muskie\",\n    \"music.game.creative.taswell\": \"C418 - Taswell\",\n    \"music.game.crescent_dunes\": \"Aaron Cherof - Crescent Dunes\",\n    \"music.game.danny\": \"C418 - Danny\",\n    \"music.game.deeper\": \"Lena Raine - Deeper\",\n    \"music.game.dry_hands\": \"C418 - Dry Hands\",\n    \"music.game.echo_in_the_wind\": \"Aaron Cherof - Echo in the Wind\",\n    \"music.game.eld_unknown\": \"Lena Raine - Eld Unknown\",\n    \"music.game.end.alpha\": \"C418 - Alpha\",\n    \"music.game.end.boss\": \"C418 - Boss\",\n    \"music.game.end.the_end\": \"C418 - The End\",\n    \"music.game.endless\": \"Lena Raine - Endless\",\n    \"music.game.featherfall\": \"Aaron Cherof - Featherfall\",\n    \"music.game.fireflies\": \"Amos Roddy - Fireflies\",\n    \"music.game.floating_dream\": \"Kumi Tanioka - Floating Dream\",\n    \"music.game.haggstrom\": \"C418 - Haggstrom\",\n    \"music.game.infinite_amethyst\": \"Lena Raine - Infinite Amethyst\",\n    \"music.game.key\": \"C418 - Key\",\n    \"music.game.komorebi\": \"Kumi Tanioka - komorebi\",\n    \"music.game.left_to_bloom\": \"Lena Raine - Left to Bloom\",\n    \"music.game.lilypad\": \"Amos Roddy - Lilypad\",\n    \"music.game.living_mice\": \"C418 - Living Mice\",\n    \"music.game.mice_on_venus\": \"C418 - Mice on Venus\",\n    \"music.game.minecraft\": \"C418 - Minecraft\",\n    \"music.game.nether.ballad_of_the_cats\": \"C418 - Ballad of the Cats\",\n    \"music.game.nether.concrete_halls\": \"C418 - Concrete Halls\",\n    \"music.game.nether.crimson_forest.chrysopoeia\": \"Lena Raine - Chrysopoeia\",\n    \"music.game.nether.dead_voxel\": \"C418 - Dead Voxel\",\n    \"music.game.nether.nether_wastes.rubedo\": \"Lena Raine - Rubedo\",\n    \"music.game.nether.soulsand_valley.so_below\": \"Lena Raine - So Below\",\n    \"music.game.nether.warmth\": \"C418 - Warmth\",\n    \"music.game.one_more_day\": \"Lena Raine - One More Day\",\n    \"music.game.os_piano\": \"Amos Roddy - O's Piano\",\n    \"music.game.oxygene\": \"C418 - Oxygène\",\n    \"music.game.pokopoko\": \"Kumi Tanioka - pokopoko\",\n    \"music.game.puzzlebox\": \"Aaron Cherof - Puzzlebox\",\n    \"music.game.stand_tall\": \"Lena Raine - Stand Tall\",\n    \"music.game.subwoofer_lullaby\": \"C418 - Subwoofer Lullaby\",\n    \"music.game.swamp.aerie\": \"Lena Raine - Aerie\",\n    \"music.game.swamp.firebugs\": \"Lena Raine - Firebugs\",\n    \"music.game.swamp.labyrinthine\": \"Lena Raine - Labyrinthine\",\n    \"music.game.sweden\": \"C418 - Sweden\",\n    \"music.game.watcher\": \"Aaron Cherof - Watcher\",\n    \"music.game.water.axolotl\": \"C418 - Axolotl\",\n    \"music.game.water.dragon_fish\": \"C418 - Dragon Fish\",\n    \"music.game.water.shuniji\": \"C418 - Shuniji\",\n    \"music.game.wending\": \"Lena Raine - Wending\",\n    \"music.game.wet_hands\": \"C418 - Wet Hands\",\n    \"music.game.yakusoku\": \"Kumi Tanioka - yakusoku\",\n    \"music.menu.beginning_2\": \"C418 - Beginning 2\",\n    \"music.menu.floating_trees\": \"C418 - Floating Trees\",\n    \"music.menu.moog_city_2\": \"C418 - Moog City 2\",\n    \"music.menu.mutation\": \"C418 - Mutation\",\n    \"narration.item\": \"Item: %s\",\n    \"options.music_frequency\": \"Music Frequency\",\n    \"options.music_frequency.constant\": \"Constant\",\n    \"options.music_frequency.frequent\": \"Frequent\",\n    \"options.music_frequency.tooltip\": \"Changes how frequently music plays while in a game world.\",\n    \"options.renderCloudsDistance\": \"Cloud Distance\",\n    \"options.showNowPlayingToast\": \"Show Music Toast\",\n    \"options.showNowPlayingToast.tooltip\": \"Displays a toast whenever a song starts playing. The same toast is constantly displayed in the in-game pause menu while a song is playing.\",\n    \"realms.configuration.region_preference.automatic_owner\": \"Automatic (Realm owner ping)\",\n    \"realms.configuration.region_preference.automatic_player\": \"Automatic (first to join session)\",\n    \"realms.configuration.region.australia_east\": \"New South Wales, Australia\",\n    \"realms.configuration.region.australia_southeast\": \"Victoria, Australia\",\n    \"realms.configuration.region.brazil_south\": \"Brazil\",\n    \"realms.configuration.region.central_india\": \"India\",\n    \"realms.configuration.region.central_us\": \"Iowa, USA\",\n    \"realms.configuration.region.east_asia\": \"Hong Kong\",\n    \"realms.configuration.region.east_us\": \"Virginia, USA\",\n    \"realms.configuration.region.east_us_2\": \"North Carolina, USA\",\n    \"realms.configuration.region.france_central\": \"France\",\n    \"realms.configuration.region.japan_east\": \"Eastern Japan\",\n    \"realms.configuration.region.japan_west\": \"Western Japan\",\n    \"realms.configuration.region.korea_central\": \"South Korea\",\n    \"realms.configuration.region.north_central_us\": \"Illinois, USA\",\n    \"realms.configuration.region.north_europe\": \"Ireland\",\n    \"realms.configuration.region.south_central_us\": \"Texas, USA\",\n    \"realms.configuration.region.southeast_asia\": \"Singapore\",\n    \"realms.configuration.region.sweden_central\": \"Sweden\",\n    \"realms.configuration.region.uae_north\": \"United Arab Emirates (UAE)\",\n    \"realms.configuration.region.uk_south\": \"Southern England\",\n    \"realms.configuration.region.west_central_us\": \"Utah, USA\",\n    \"realms.configuration.region.west_europe\": \"Netherlands\",\n    \"realms.configuration.region.west_us\": \"California, USA\",\n    \"realms.configuration.region.west_us_2\": \"Washington, USA\",\n    \"soundCategory.ui\": \"UI\",\n    \"stat.minecraft.happy_ghast_one_cm\": \"Distance by Happy Ghast\",\n    \"subtitles.block.dried_ghast.ambient\": \"Sounds of dryness\",\n    \"subtitles.block.dried_ghast.ambient_water\": \"Dried Ghast rehydrates\",\n    \"subtitles.block.dried_ghast.place_in_water\": \"Dried Ghast soaks\",\n    \"subtitles.block.dried_ghast.transition\": \"Dried Ghast feels better\",\n    \"subtitles.entity.ghastling.ambient\": \"Ghastling coos\",\n    \"subtitles.entity.ghastling.death\": \"Ghastling dies\",\n    \"subtitles.entity.ghastling.hurt\": \"Ghastling hurts\",\n    \"subtitles.entity.ghastling.spawn\": \"Ghastling appears\",\n    \"subtitles.entity.happy_ghast.ambient\": \"Happy Ghast croons\",\n    \"subtitles.entity.happy_ghast.death\": \"Happy Ghast dies\",\n    \"subtitles.entity.happy_ghast.equip\": \"Harness equips\",\n    \"subtitles.entity.happy_ghast.harness_goggles_down\": \"Happy Ghast is ready\",\n    \"subtitles.entity.happy_ghast.harness_goggles_up\": \"Happy Ghast stops\",\n    \"subtitles.entity.happy_ghast.hurt\": \"Happy Ghast hurts\",\n    \"subtitles.entity.happy_ghast.unequip\": \"Harness unequips\",\n    \"subtitles.item.horse_armor.unequip\": \"Horse Armor snips away\",\n    \"subtitles.item.lead.break\": \"Lead snaps\",\n    \"subtitles.item.lead.tied\": \"Lead tied\",\n    \"subtitles.item.lead.untied\": \"Lead untied\",\n    \"subtitles.item.llama_carpet.unequip\": \"Carpet snips away\",\n    \"subtitles.item.saddle.unequip\": \"Saddle snips away\",\n    \"subtitles.item.shears.snip\": \"Shears snip\"\n  },\n  \"1.21.5\": {\n    \"argument.nbt.expected.compound\": \"Expected compound tag\",\n    \"argument.resource_selector.not_found\": \"No matches for selector '%s' of type '%s'\",\n    \"block.minecraft.bush\": \"Bush\",\n    \"block.minecraft.cactus_flower\": \"Cactus Flower\",\n    \"block.minecraft.firefly_bush\": \"Firefly Bush\",\n    \"block.minecraft.leaf_litter\": \"Leaf Litter\",\n    \"block.minecraft.short_dry_grass\": \"Short Dry Grass\",\n    \"block.minecraft.tall_dry_grass\": \"Tall Dry Grass\",\n    \"block.minecraft.test_block\": \"Test Block\",\n    \"block.minecraft.test_instance_block\": \"Test Instance Block\",\n    \"block.minecraft.tnt.disabled\": \"TNT explosions are disabled\",\n    \"block.minecraft.wildflowers\": \"Wildflowers\",\n    \"commands.test.batch.starting\": \"Starting environment %s batch %s\",\n    \"commands.test.clear.error.no_tests\": \"Could not find any tests to clear\",\n    \"commands.test.clear.success\": \"Cleared %s structure(s)\",\n    \"commands.test.coordinates.copy\": \"Click to copy to clipboard\",\n    \"commands.test.create.success\": \"Created test setup for test %s\",\n    \"commands.test.error.no_test_containing_pos\": \"Can't find a test instance that contains %s, %s, %s\",\n    \"commands.test.error.no_test_instances\": \"Found no test instances\",\n    \"commands.test.error.non_existant_test\": \"Test %s could not be found\",\n    \"commands.test.error.structure_not_found\": \"Test structure %s could not be found\",\n    \"commands.test.error.test_instance_not_found\": \"Test instance block entity could not be found\",\n    \"commands.test.error.test_instance_not_found.position\": \"Test instance block entity could not be found for test at %s, %s, %s\",\n    \"commands.test.error.too_large\": \"The structure size must be less than %s blocks along each axis\",\n    \"commands.test.locate.done\": \"Finished locating, found %s structure(s)\",\n    \"commands.test.locate.found\": \"Found structure at: %s (distance: %s)\",\n    \"commands.test.locate.started\": \"Started locating test structures, this might take a while...\",\n    \"commands.test.no_tests\": \"No tests to run\",\n    \"commands.test.relative_position\": \"Position relative to %s: %s\",\n    \"commands.test.reset.error.no_tests\": \"Could not find any tests to reset\",\n    \"commands.test.reset.success\": \"Reset %s structure(s)\",\n    \"commands.test.run.no_tests\": \"No tests found\",\n    \"commands.test.run.running\": \"Running %s test(s)...\",\n    \"commands.test.summary\": \"Game Test complete! %s test(s) were run\",\n    \"commands.test.summary.all_required_passed\": \"All required tests passed :)\",\n    \"commands.test.summary.failed\": \"%s required test(s) failed :(\",\n    \"commands.test.summary.optional_failed\": \"%s optional test(s) failed\",\n    \"gamerule.allowFireTicksAwayFromPlayer\": \"Tick fire away from players\",\n    \"gamerule.allowFireTicksAwayFromPlayer.description\": \"Controls whether or not fire and lava should be able to tick further than 8 chunks away from any player\",\n    \"gamerule.tntExplodes\": \"Allow TNT to be activated and to explode\",\n    \"item.minecraft.blue_egg\": \"Blue Egg\",\n    \"item.minecraft.brown_egg\": \"Brown Egg\",\n    \"item.minecraft.crossbow.projectile.multiple\": \"Projectile: %s x %s\",\n    \"item.minecraft.crossbow.projectile.single\": \"Projectile: %s\",\n    \"item.minecraft.firework_rocket.multiple_stars\": \"%s x %s\",\n    \"item.modifiers.saddle\": \"When saddled:\",\n    \"multiplayer.disconnect.bad_chat_index\": \"Detected missed or reordered chat message from server\",\n    \"snbt.parser.empty_key\": \"Key cannot be empty\",\n    \"snbt.parser.expected_binary_numeral\": \"Expected a binary number\",\n    \"snbt.parser.expected_decimal_numeral\": \"Expected a decimal number\",\n    \"snbt.parser.expected_float_type\": \"Expected a floating point number\",\n    \"snbt.parser.expected_hex_escape\": \"Expected a character literal of length %s\",\n    \"snbt.parser.expected_hex_numeral\": \"Expected a hexadecimal number\",\n    \"snbt.parser.expected_integer_type\": \"Expected an integer number\",\n    \"snbt.parser.expected_non_negative_number\": \"Expected a non-negative number\",\n    \"snbt.parser.expected_number_or_boolean\": \"Expected a number or a boolean\",\n    \"snbt.parser.expected_string_uuid\": \"Expected a string representing a valid UUID\",\n    \"snbt.parser.expected_unquoted_string\": \"Expected a valid unquoted string\",\n    \"snbt.parser.infinity_not_allowed\": \"Non-finite numbers are not allowed\",\n    \"snbt.parser.invalid_array_element_type\": \"Invalid array element type\",\n    \"snbt.parser.invalid_character_name\": \"Invalid Unicode character name\",\n    \"snbt.parser.invalid_codepoint\": \"Invalid Unicode character value: %s\",\n    \"snbt.parser.invalid_string_contents\": \"Invalid string contents\",\n    \"snbt.parser.invalid_unquoted_start\": \"Unquoted strings can't start with digits 0-9, + or -\",\n    \"snbt.parser.leading_zero_not_allowed\": \"Decimal numbers can't start with 0\",\n    \"snbt.parser.no_such_operation\": \"No such operation: %s\",\n    \"snbt.parser.number_parse_failure\": \"Failed to parse number: %s\",\n    \"snbt.parser.undescore_not_allowed\": \"Underscore characters are not allowed at the start or end of a number\",\n    \"structure_block.strict\": \"Strict Placement:\",\n    \"subtitles.block.deadbush.idle\": \"Dry sounds\",\n    \"subtitles.block.firefly_bush.idle\": \"Fireflies buzz\",\n    \"subtitles.block.sand.idle\": \"Sandy sounds\",\n    \"subtitles.block.sand.wind\": \"Windy sounds\",\n    \"subtitles.entity.wolf.bark\": \"Wolf barks\",\n    \"subtitles.entity.wolf.whine\": \"Wolf whines\",\n    \"test_block.error.missing\": \"Test structure missing %s block\",\n    \"test_block.error.too_many\": \"Too many %s blocks\",\n    \"test_block.invalid_timeout\": \"Invalid timeout (%s) - must be a positive number of ticks\",\n    \"test_block.message\": \"Message:\",\n    \"test_block.mode_info.accept\": \"Accept Mode - Accept success for (part of) a test\",\n    \"test_block.mode_info.fail\": \"Fail Mode - Fail the test\",\n    \"test_block.mode_info.log\": \"Log Mode - Log a message\",\n    \"test_block.mode_info.start\": \"Start Mode - The starting point for a test\",\n    \"test_block.mode.fail\": \"Fail\",\n    \"test_block.mode.log\": \"Log\",\n    \"test_block.mode.start\": \"Start\",\n    \"test_instance_block.entities\": \"Entities:\",\n    \"test_instance_block.error.no_test\": \"Unable to run test instance at %s, %s, %s since it has an undefined test\",\n    \"test_instance_block.error.no_test_structure\": \"Unable to run test instance at %s, %s, %s since it has no test structure\",\n    \"test_instance_block.error.unable_to_save\": \"Unable to save test structure template for test instance at %s, %s, %s\",\n    \"test_instance_block.invalid\": \"[invalid]\",\n    \"test_instance_block.reset_success\": \"Reset succeeded for test: %s\",\n    \"test_instance_block.rotation\": \"Rotation:\",\n    \"test_instance_block.size\": \"Test Structure Size\",\n    \"test_instance_block.starting\": \"Starting test %s\",\n    \"test_instance_block.test_id\": \"Test Instance ID\",\n    \"test_instance.action.reset\": \"Reset and Load\",\n    \"test_instance.action.run\": \"Load and Run\",\n    \"test_instance.action.save\": \"Save Structure\",\n    \"test_instance.description.batch\": \"Batch: %s\",\n    \"test_instance.description.failed\": \"Failed: %s\",\n    \"test_instance.description.function\": \"Function: %s\",\n    \"test_instance.description.invalid_id\": \"Invalid test ID\",\n    \"test_instance.description.no_test\": \"No such test\",\n    \"test_instance.description.structure\": \"Structure: %s\",\n    \"test_instance.type.block_based\": \"Block-Based Test\",\n    \"test_instance.type.function\": \"Built-in Function Test\",\n    \"test.error.block_property_mismatch\": \"Expected property %s to be %s, was %s\",\n    \"test.error.block_property_missing\": \"Block property missing, expected property %s to be %s\",\n    \"test.error.entity_property\": \"Entity %s failed test: %s\",\n    \"test.error.entity_property_details\": \"Entity %s failed test: %s, expected: %s, was: %s\",\n    \"test.error.expected_block\": \"Expected block %s, got %s\",\n    \"test.error.expected_block_tag\": \"Expected block in #%s, got %s\",\n    \"test.error.expected_container_contents\": \"Container should contain: %s\",\n    \"test.error.expected_container_contents_single\": \"Container should contain a single: %s\",\n    \"test.error.expected_empty_container\": \"Container should be empty\",\n    \"test.error.expected_entity\": \"Expected %s\",\n    \"test.error.expected_entity_around\": \"Expected %s to exist around %s, %s, %s\",\n    \"test.error.expected_entity_count\": \"Expected %s entities of type %s, found %s\",\n    \"test.error.expected_entity_data\": \"Expected entity data to be: %s, was: %s\",\n    \"test.error.expected_entity_data_predicate\": \"Entity data mismatch for %s\",\n    \"test.error.expected_entity_effect\": \"Expected %s to have effect %s %s\",\n    \"test.error.expected_entity_having\": \"Entity inventory should contain %s\",\n    \"test.error.expected_entity_holding\": \"Entity should be holding %s\",\n    \"test.error.expected_entity_in_test\": \"Expected %s to exist in test\",\n    \"test.error.expected_entity_not_touching\": \"Did not expect %s touching %s, %s, %s (relative: %s, %s, %s)\",\n    \"test.error.expected_entity_touching\": \"Expected %s touching %s, %s, %s (relative: %s, %s, %s)\",\n    \"test.error.expected_item\": \"Expected item of type %s\",\n    \"test.error.expected_items_count\": \"Expected %s items of type %s, found %s\",\n    \"test.error.fail\": \"Fail conditions met\",\n    \"test.error.invalid_block_type\": \"Unexpected block type found: %s\",\n    \"test.error.missing_block_entity\": \"Missing block entity\",\n    \"test.error.position\": \"%s at %s, %s, %s (relative: %s, %s, %s) on tick %s\",\n    \"test.error.sequence.condition_already_triggered\": \"Condition already triggered at %s\",\n    \"test.error.sequence.condition_not_triggered\": \"Condition not triggered\",\n    \"test.error.sequence.invalid_tick\": \"Succeeded in invalid tick: expected %s\",\n    \"test.error.sequence.not_completed\": \"Test timed out before sequence completed\",\n    \"test.error.set_biome\": \"Failed to set biome for test\",\n    \"test.error.spawn_failure\": \"Failed to create entity %s\",\n    \"test.error.state_not_equal\": \"Incorrect state. Expected %s, was %s\",\n    \"test.error.structure.failure\": \"Failed to place test structure for %s\",\n    \"test.error.tick\": \"%s on tick %s\",\n    \"test.error.ticking_without_structure\": \"Ticking test before placing structure\",\n    \"test.error.timeout.no_result\": \"Didn't succeed or fail within %s ticks\",\n    \"test.error.timeout.no_sequences_finished\": \"No sequences finished within %s ticks\",\n    \"test.error.too_many_entities\": \"Expected only one %s to exist around %s, %s, %s but found %s\",\n    \"test.error.unexpected_block\": \"Did not expect block to be %s\",\n    \"test.error.unexpected_entity\": \"Did not expect %s to exist\",\n    \"test.error.unexpected_item\": \"Did not expect item of type %s\",\n    \"test.error.unknown\": \"Unknown internal error: %s\",\n    \"test.error.value_not_equal\": \"Expected %s to be %s, was %s\",\n    \"test.error.wrong_block_entity\": \"Wrong block entity type: %s\"\n  },\n  \"1.21.4\": {\n    \"block.minecraft.chiseled_resin_bricks\": \"Chiseled Resin Bricks\",\n    \"block.minecraft.closed_eyeblossom\": \"Closed Eyeblossom\",\n    \"block.minecraft.open_eyeblossom\": \"Open Eyeblossom\",\n    \"block.minecraft.potted_closed_eyeblossom\": \"Potted Closed Eyeblossom\",\n    \"block.minecraft.potted_open_eyeblossom\": \"Potted Open Eyeblossom\",\n    \"block.minecraft.resin_block\": \"Block of Resin\",\n    \"block.minecraft.resin_brick_slab\": \"Resin Brick Slab\",\n    \"block.minecraft.resin_brick_stairs\": \"Resin Brick Stairs\",\n    \"block.minecraft.resin_brick_wall\": \"Resin Brick Wall\",\n    \"block.minecraft.resin_bricks\": \"Resin Bricks\",\n    \"block.minecraft.resin_clump\": \"Resin Clump\",\n    \"commands.attribute.base_value.reset.success\": \"Base value for attribute %s for entity %s reset to default %s\",\n    \"item.minecraft.resin_brick\": \"Resin Brick\",\n    \"item.minecraft.resin_clump\": \"Resin Clump\",\n    \"item.op_block_warning.line1\": \"Warning:\",\n    \"item.op_block_warning.line2\": \"Use of this item might lead to command execution\",\n    \"item.op_block_warning.line3\": \"Do not use unless you know the exact contents!\",\n    \"subtitles.block.eyeblossom.close\": \"Eyeblossom closes\",\n    \"subtitles.block.eyeblossom.idle\": \"Eyeblossom whispers\",\n    \"subtitles.block.eyeblossom.open\": \"Eyeblossom opens\",\n    \"subtitles.block.generic.fall\": \"Something falls on a block\",\n    \"subtitles.entity.creaking.twitch\": \"Creaking twitches\",\n    \"subtitles.entity.fish.swim\": \"Splashes\",\n    \"subtitles.entity.minecart.inside\": \"Minecart jangles\",\n    \"subtitles.entity.minecart.inside_underwater\": \"Minecart jangles underwater\",\n    \"subtitles.entity.skeleton_horse.jump_water\": \"Skeleton Horse jumps\",\n    \"subtitles.item.elytra.flying\": \"Swoosh\",\n    \"trim_material.minecraft.resin\": \"Resin Material\"\n  },\n  \"1.21.2\": {\n    \"attribute.name.tempt_range\": \"Mob Tempt Range\",\n    \"block.minecraft.creaking_heart\": \"Creaking Heart\",\n    \"block.minecraft.pale_hanging_moss\": \"Pale Hanging Moss\",\n    \"block.minecraft.pale_moss_block\": \"Pale Moss Block\",\n    \"block.minecraft.pale_moss_carpet\": \"Pale Moss Carpet\",\n    \"block.minecraft.pale_oak_button\": \"Pale Oak Button\",\n    \"block.minecraft.pale_oak_door\": \"Pale Oak Door\",\n    \"block.minecraft.pale_oak_fence\": \"Pale Oak Fence\",\n    \"block.minecraft.pale_oak_fence_gate\": \"Pale Oak Fence Gate\",\n    \"block.minecraft.pale_oak_hanging_sign\": \"Pale Oak Hanging Sign\",\n    \"block.minecraft.pale_oak_leaves\": \"Pale Oak Leaves\",\n    \"block.minecraft.pale_oak_log\": \"Pale Oak Log\",\n    \"block.minecraft.pale_oak_planks\": \"Pale Oak Planks\",\n    \"block.minecraft.pale_oak_pressure_plate\": \"Pale Oak Pressure Plate\",\n    \"block.minecraft.pale_oak_sapling\": \"Pale Oak Sapling\",\n    \"block.minecraft.pale_oak_sign\": \"Pale Oak Sign\",\n    \"block.minecraft.pale_oak_slab\": \"Pale Oak Slab\",\n    \"block.minecraft.pale_oak_stairs\": \"Pale Oak Stairs\",\n    \"block.minecraft.pale_oak_trapdoor\": \"Pale Oak Trapdoor\",\n    \"block.minecraft.pale_oak_wood\": \"Pale Oak Wood\",\n    \"block.minecraft.potted_pale_oak_sapling\": \"Potted Pale Oak Sapling\",\n    \"block.minecraft.stripped_pale_oak_log\": \"Stripped Pale Oak Log\",\n    \"block.minecraft.stripped_pale_oak_wood\": \"Stripped Pale Oak Wood\",\n    \"commands.drop.no_loot_table.block\": \"Block %s has no loot table\",\n    \"commands.rotate.success\": \"Rotated %s\",\n    \"commands.schedule.macro\": \"Can't schedule a macro\",\n    \"commands.setidletimeout.success.disabled\": \"The player idle timeout is now disabled\",\n    \"container.beehive.bees\": \"Bees: %s / %s\",\n    \"container.beehive.honey\": \"Honey: %s / %s\",\n    \"dataPack.minecart_improvements.description\": \"Improved movement for Minecarts\",\n    \"dataPack.minecart_improvements.name\": \"Minecart Improvements\",\n    \"dataPack.redstone_experiments.description\": \"Experimental Redstone changes\",\n    \"dataPack.redstone_experiments.name\": \"Redstone Experiments\",\n    \"dataPack.winter_drop.description\": \"New features and content for the Winter Drop\",\n    \"dataPack.winter_drop.name\": \"Winter Drop\",\n    \"death.attack.mace_smash\": \"%1$s was smashed by %2$s\",\n    \"death.attack.mace_smash.item\": \"%1$s was smashed by %2$s with %3$s\",\n    \"entity.minecraft.creaking\": \"Creaking\",\n    \"entity.minecraft.creaking_transient\": \"Creaking\",\n    \"entity.minecraft.pale_oak_boat\": \"Pale Oak Boat\",\n    \"entity.minecraft.pale_oak_chest_boat\": \"Pale Oak Boat with Chest\",\n    \"gamerule.disablePlayerMovementCheck\": \"Disable player movement check\",\n    \"gamerule.minecartMaxSpeed\": \"Minecart max speed\",\n    \"gamerule.minecartMaxSpeed.description\": \"Maximum default speed of a moving Minecart on land.\",\n    \"gui.abuseReport.name.comment_box_label\": \"Please describe why you want to report this name:\",\n    \"gui.abuseReport.reason.sexually_inappropriate\": \"Sexually inappropriate\",\n    \"gui.abuseReport.reason.sexually_inappropriate.description\": \"Skins that are graphic in nature relating to sexual acts, sexual organs, and sexual violence.\",\n    \"item.minecraft.black_bundle\": \"Black Bundle\",\n    \"item.minecraft.blue_bundle\": \"Blue Bundle\",\n    \"item.minecraft.bordure_indented_banner_pattern\": \"Bordure Indented Banner Pattern\",\n    \"item.minecraft.brown_bundle\": \"Brown Bundle\",\n    \"item.minecraft.bundle.empty.description\": \"Can hold a mixed stack of items\",\n    \"item.minecraft.bundle.full\": \"Full\",\n    \"item.minecraft.creaking_spawn_egg\": \"Creaking Spawn Egg\",\n    \"item.minecraft.creeper_banner_pattern.new\": \"Creeper Charge Banner Pattern\",\n    \"item.minecraft.cyan_bundle\": \"Cyan Bundle\",\n    \"item.minecraft.field_masoned_banner_pattern\": \"Field Masoned Banner Pattern\",\n    \"item.minecraft.flow_banner_pattern.new\": \"Flow Banner Pattern\",\n    \"item.minecraft.flower_banner_pattern.new\": \"Flower Charge Banner Pattern\",\n    \"item.minecraft.globe_banner_pattern.new\": \"Globe Banner Pattern\",\n    \"item.minecraft.gray_bundle\": \"Gray Bundle\",\n    \"item.minecraft.green_bundle\": \"Green Bundle\",\n    \"item.minecraft.guster_banner_pattern.new\": \"Guster Banner Pattern\",\n    \"item.minecraft.light_blue_bundle\": \"Light Blue Bundle\",\n    \"item.minecraft.light_gray_bundle\": \"Light Gray Bundle\",\n    \"item.minecraft.lime_bundle\": \"Lime Bundle\",\n    \"item.minecraft.magenta_bundle\": \"Magenta Bundle\",\n    \"item.minecraft.mojang_banner_pattern.new\": \"Thing Banner Pattern\",\n    \"item.minecraft.orange_bundle\": \"Orange Bundle\",\n    \"item.minecraft.pale_oak_boat\": \"Pale Oak Boat\",\n    \"item.minecraft.pale_oak_chest_boat\": \"Pale Oak Boat with Chest\",\n    \"item.minecraft.piglin_banner_pattern.new\": \"Snout Banner Pattern\",\n    \"item.minecraft.pink_bundle\": \"Pink Bundle\",\n    \"item.minecraft.purple_bundle\": \"Purple Bundle\",\n    \"item.minecraft.red_bundle\": \"Red Bundle\",\n    \"item.minecraft.skull_banner_pattern.new\": \"Skull Charge Banner Pattern\",\n    \"item.minecraft.white_bundle\": \"White Bundle\",\n    \"item.minecraft.yellow_bundle\": \"Yellow Bundle\",\n    \"mco.create.world.failed\": \"Failed to create world!\",\n    \"mco.errorMessage.initialize.failed\": \"Failed to initialize Realm\",\n    \"mco.upload.failed.too_big.description\": \"The selected world is too big. The maximum allowed size is %s.\",\n    \"mco.upload.failed.too_big.title\": \"World too big\",\n    \"optimizeWorld.confirm.proceed\": \"Create Backup and Optimize\",\n    \"options.accessibility.high_contrast_block_outline\": \"High Contrast Block Outlines\",\n    \"options.accessibility.high_contrast_block_outline.tooltip\": \"Enhances the block outline contrast of targeted block.\",\n    \"options.inactivityFpsLimit\": \"Reduce FPS when\",\n    \"options.inactivityFpsLimit.afk\": \"AFK\",\n    \"options.inactivityFpsLimit.afk.tooltip\": \"Limits framerate to 30 when the game is not getting any player input for more than a minute. Further limits it to 10 after 9 more minutes.\",\n    \"options.inactivityFpsLimit.minimized\": \"Minimized\",\n    \"options.inactivityFpsLimit.minimized.tooltip\": \"Limits framerate only when the game window is minimized.\",\n    \"options.rotateWithMinecart\": \"Rotate with Minecarts\",\n    \"options.rotateWithMinecart.tooltip\": \"Whether the player's view should rotate with a turning Minecart. Only available in worlds with the 'Minecart Improvements' experimental setting turned on.\",\n    \"resourcePack.runtime_failure\": \"Resource pack error detected\",\n    \"subtitles.block.creaking_heart.hurt\": \"Creaking Heart screams\",\n    \"subtitles.block.creaking_heart.spawn\": \"Creaking Heart awakens\",\n    \"subtitles.entity.creaking.activate\": \"Creaking activates\",\n    \"subtitles.entity.creaking.ambient\": \"Creaking creaks\",\n    \"subtitles.entity.creaking.angry\": \"Creaking sees player\",\n    \"subtitles.entity.creaking.attack\": \"Creaking attacks\",\n    \"subtitles.entity.creaking.deactivate\": \"Creaking deactivates\",\n    \"subtitles.entity.creaking.death\": \"Creaking dies\",\n    \"subtitles.entity.creaking.freeze\": \"Creaking stops\",\n    \"subtitles.entity.creaking.spawn\": \"Creaking lives\",\n    \"subtitles.entity.creaking.sway\": \"Creaking is shielded\",\n    \"subtitles.entity.creaking.unfreeze\": \"Creaking moves\",\n    \"subtitles.entity.parrot.imitate.creaking\": \"Parrot creaks\",\n    \"subtitles.item.bundle.insert_fail\": \"Bundle full\",\n    \"subtitles.ui.hud.bubble_pop\": \"Breath meter dropping\"\n  },\n  \"1.21\": {\n    \"argument.entity.selector.nearestEntity\": \"Nearest entity\",\n    \"attribute.name.generic.burning_time\": \"Burning Time\",\n    \"attribute.name.generic.explosion_knockback_resistance\": \"Explosion Knockback Resistance\",\n    \"attribute.name.generic.movement_efficiency\": \"Movement Efficiency\",\n    \"attribute.name.generic.oxygen_bonus\": \"Oxygen Bonus\",\n    \"attribute.name.generic.water_movement_efficiency\": \"Water Movement Efficiency\",\n    \"attribute.name.player.mining_efficiency\": \"Mining Efficiency\",\n    \"attribute.name.player.sneaking_speed\": \"Sneaking Speed\",\n    \"attribute.name.player.submerged_mining_speed\": \"Submerged Mining Speed\",\n    \"attribute.name.player.sweeping_damage_ratio\": \"Sweeping Damage Ratio\",\n    \"gamerule.entitiesWithPassengersCanUsePortals\": \"Entities with passengers can use portals\",\n    \"gamerule.entitiesWithPassengersCanUsePortals.description\": \"Allow entities with passengers to teleport through Nether Portals, End Portals, and End Gateways.\",\n    \"gui.abuseReport.attestation\": \"By submitting this report, you confirm that the information you have provided is accurate and complete to the best of your knowledge.\",\n    \"gui.abuseReport.send.not_attested\": \"Please read the text above and tick the checkbox to be able to send the report\",\n    \"gui.fileDropFailure.detail\": \"Rejected %d files\",\n    \"gui.fileDropFailure.title\": \"Failed to add files\",\n    \"gui.open_report_dir\": \"Open Report Directory\",\n    \"gui.report_to_server\": \"Report To Server\",\n    \"item.minecraft.music_disc_creator_music_box.desc\": \"Lena Raine - Creator (Music Box)\",\n    \"item.minecraft.music_disc_creator.desc\": \"Lena Raine - Creator\",\n    \"item.minecraft.music_disc_precipice.desc\": \"Aaron Cherof - Precipice\",\n    \"item.modifiers.armor\": \"When worn:\",\n    \"item.modifiers.hand\": \"When held:\",\n    \"jukebox_song.minecraft.creator\": \"Lena Raine - Creator\",\n    \"jukebox_song.minecraft.creator_music_box\": \"Lena Raine - Creator (Music Box)\",\n    \"jukebox_song.minecraft.precipice\": \"Aaron Cherof - Precipice\",\n    \"known_server_link.announcements\": \"Announcements\",\n    \"known_server_link.community\": \"Community\",\n    \"known_server_link.community_guidelines\": \"Community Guidelines\",\n    \"known_server_link.feedback\": \"Feedback\",\n    \"known_server_link.forums\": \"Forums\",\n    \"known_server_link.news\": \"News\",\n    \"known_server_link.report_bug\": \"Report Server Bug\",\n    \"known_server_link.support\": \"Support\",\n    \"known_server_link.website\": \"Website\",\n    \"mco.compatibility.incompatible.releaseType.popup.message\": \"The world you are trying to join is incompatible with the version you are on.\",\n    \"mco.compatibility.incompatible.series.popup.message\": \"This world was last played in version %s; you are on version %s.\\n\\nThese series are not compatible with each other. A new world is needed to play on this version.\",\n    \"mco.compatibility.upgrade.friend.description\": \"This world was last played in version %s; you are on version %s.\\n\\nA backup of the world will be saved under \\\"World Backups\\\".\\n\\nThe owner of the Realm can restore the world if needed.\",\n    \"mco.configure.world.resourcepack.question\": \"You need a custom resource pack to play on this realm\\n\\nDo you want to download it and play?\",\n    \"mco.download.confirmation.oversized\": \"The world you are going to download is larger than %s\\n\\nYou won't be able to upload this world to your realm again\",\n    \"mco.onlinePlayers\": \"Online Players\",\n    \"menu.feedback\": \"Feedback...\",\n    \"menu.feedback.title\": \"Feedback\",\n    \"menu.server_links\": \"Server Links...\",\n    \"menu.server_links.title\": \"Server Links\",\n    \"options.realmsNotifications.tooltip\": \"Fetches Realms news and invites in the title screen and displays their respective icon on the Realms button.\",\n    \"painting.minecraft.backyard.title\": \"Backyard\",\n    \"painting.minecraft.baroque.author\": \"Sarah Boeving\",\n    \"painting.minecraft.baroque.title\": \"Baroque\",\n    \"painting.minecraft.bouquet.title\": \"Bouquet\",\n    \"painting.minecraft.cavebird.title\": \"Cavebird\",\n    \"painting.minecraft.changing.title\": \"Changing\",\n    \"painting.minecraft.cotan.title\": \"Cot�n\",\n    \"painting.minecraft.endboss.title\": \"Endboss\",\n    \"painting.minecraft.finding.title\": \"Finding\",\n    \"painting.minecraft.humble.author\": \"Sarah Boeving\",\n    \"painting.minecraft.humble.title\": \"Humble\",\n    \"painting.minecraft.lowmist.title\": \"Lowmist\",\n    \"painting.minecraft.meditative.author\": \"Sarah Boeving\",\n    \"painting.minecraft.meditative.title\": \"Meditative\",\n    \"painting.minecraft.orb.title\": \"Orb\",\n    \"painting.minecraft.owlemons.title\": \"Owlemons\",\n    \"painting.minecraft.passage.title\": \"Passage\",\n    \"painting.minecraft.pond.title\": \"Pond\",\n    \"painting.minecraft.prairie_ride.author\": \"Sarah Boeving\",\n    \"painting.minecraft.prairie_ride.title\": \"Prairie Ride\",\n    \"painting.minecraft.sunflowers.title\": \"Sunflowers\",\n    \"painting.minecraft.tides.title\": \"Tides\",\n    \"painting.minecraft.unpacked.author\": \"Sarah Boeving\",\n    \"painting.minecraft.unpacked.title\": \"Unpacked\",\n    \"subtitles.block.trial_spawner.ambient_ominous\": \"Ominous crackling\",\n    \"subtitles.block.vault.reject_rewarded_player\": \"Vault rejects player\"\n  },\n  \"1.20.5\": {\n    \"advancements.adventure.blowback.description\": \"Kill a Breeze with a deflected Breeze-shot Wind Charge\",\n    \"advancements.adventure.blowback.title\": \"Blowback\",\n    \"advancements.adventure.brush_armadillo.description\": \"Get Armadillo Scutes from an Armadillo using a Brush\",\n    \"advancements.adventure.brush_armadillo.title\": \"Isn't It Scute?\",\n    \"advancements.adventure.crafters_crafting_crafters.description\": \"Be near a Crafter when it crafts a Crafter\",\n    \"advancements.adventure.crafters_crafting_crafters.title\": \"Crafters Crafting Crafters\",\n    \"advancements.adventure.lighten_up.description\": \"Scrape a Copper Bulb with an Axe to make it brighter\",\n    \"advancements.adventure.lighten_up.title\": \"Lighten Up\",\n    \"advancements.adventure.minecraft_trials_edition.description\": \"Step foot in a Trial Chamber\",\n    \"advancements.adventure.minecraft_trials_edition.title\": \"Minecraft: Trial(s) Edition\",\n    \"advancements.adventure.overoverkill.description\": \"Deal 50 hearts of damage in a single hit using the Mace\",\n    \"advancements.adventure.overoverkill.title\": \"Over-Overkill\",\n    \"advancements.adventure.revaulting.description\": \"Unlock an Ominous Vault with an Ominous Trial Key\",\n    \"advancements.adventure.revaulting.title\": \"Revaulting\",\n    \"advancements.adventure.under_lock_and_key.description\": \"Unlock a Vault with a Trial Key\",\n    \"advancements.adventure.under_lock_and_key.title\": \"Under Lock and Key\",\n    \"advancements.adventure.who_needs_rockets.description\": \"Use a Wind Charge to launch yourself upward 8 blocks\",\n    \"advancements.adventure.who_needs_rockets.title\": \"Who Needs Rockets?\",\n    \"advancements.husbandry.remove_wolf_armor.description\": \"Remove Wolf Armor from a Wolf using Shears\",\n    \"advancements.husbandry.remove_wolf_armor.title\": \"Shear Brilliance\",\n    \"advancements.husbandry.repair_wolf_armor.description\": \"Repair a damaged Wolf Armor using Armadillo Scutes\",\n    \"advancements.husbandry.repair_wolf_armor.title\": \"Good as New\",\n    \"advancements.husbandry.whole_pack.description\": \"Tame one of each Wolf variant\",\n    \"advancements.husbandry.whole_pack.title\": \"The Whole Pack\",\n    \"argument.message.too_long\": \"Chat message was too long (%s > maximum %s characters)\",\n    \"argument.resource_or_id.failed_to_parse\": \"Failed to parse structure: %s\",\n    \"argument.resource_or_id.invalid\": \"Invalid id or tag\",\n    \"arguments.item.component.expected\": \"Expected item component\",\n    \"arguments.item.component.malformed\": \"Malformed '%s' component: '%s'\",\n    \"arguments.item.component.repeated\": \"Item component '%s' was repeated, but only one value can be specified\",\n    \"arguments.item.component.unknown\": \"Unknown item component '%s'\",\n    \"arguments.item.malformed\": \"Malformed item: '%s'\",\n    \"arguments.item.predicate.malformed\": \"Malformed '%s' predicate: '%s'\",\n    \"arguments.item.predicate.unknown\": \"Unknown item predicate '%s'\",\n    \"attribute.name.generic.block_interaction_range\": \"Block Interaction Range\",\n    \"attribute.name.generic.entity_interaction_range\": \"Entity Interaction Range\",\n    \"attribute.name.generic.fall_damage_multiplier\": \"Fall Damage Multiplier\",\n    \"attribute.name.generic.gravity\": \"Gravity\",\n    \"attribute.name.generic.jump_strength\": \"Jump Strength\",\n    \"attribute.name.generic.safe_fall_distance\": \"Safe Fall Distance\",\n    \"attribute.name.generic.scale\": \"Scale\",\n    \"attribute.name.generic.step_height\": \"Step Height\",\n    \"attribute.name.player.block_break_speed\": \"Block Break Speed\",\n    \"attribute.name.player.block_interaction_range\": \"Block Interaction Range\",\n    \"attribute.name.player.entity_interaction_range\": \"Entity Interaction Range\",\n    \"block.minecraft.banner.flow.black\": \"Black Flow\",\n    \"block.minecraft.banner.flow.blue\": \"Blue Flow\",\n    \"block.minecraft.banner.flow.brown\": \"Brown Flow\",\n    \"block.minecraft.banner.flow.cyan\": \"Cyan Flow\",\n    \"block.minecraft.banner.flow.gray\": \"Gray Flow\",\n    \"block.minecraft.banner.flow.green\": \"Green Flow\",\n    \"block.minecraft.banner.flow.light_blue\": \"Light Blue Flow\",\n    \"block.minecraft.banner.flow.light_gray\": \"Light Gray Flow\",\n    \"block.minecraft.banner.flow.lime\": \"Lime Flow\",\n    \"block.minecraft.banner.flow.magenta\": \"Magenta Flow\",\n    \"block.minecraft.banner.flow.orange\": \"Orange Flow\",\n    \"block.minecraft.banner.flow.pink\": \"Pink Flow\",\n    \"block.minecraft.banner.flow.purple\": \"Purple Flow\",\n    \"block.minecraft.banner.flow.red\": \"Red Flow\",\n    \"block.minecraft.banner.flow.white\": \"White Flow\",\n    \"block.minecraft.banner.flow.yellow\": \"Yellow Flow\",\n    \"block.minecraft.banner.guster.black\": \"Black Guster\",\n    \"block.minecraft.banner.guster.blue\": \"Blue Guster\",\n    \"block.minecraft.banner.guster.brown\": \"Brown Guster\",\n    \"block.minecraft.banner.guster.cyan\": \"Cyan Guster\",\n    \"block.minecraft.banner.guster.gray\": \"Gray Guster\",\n    \"block.minecraft.banner.guster.green\": \"Green Guster\",\n    \"block.minecraft.banner.guster.light_blue\": \"Light Blue Guster\",\n    \"block.minecraft.banner.guster.light_gray\": \"Light Gray Guster\",\n    \"block.minecraft.banner.guster.lime\": \"Lime Guster\",\n    \"block.minecraft.banner.guster.magenta\": \"Magenta Guster\",\n    \"block.minecraft.banner.guster.orange\": \"Orange Guster\",\n    \"block.minecraft.banner.guster.pink\": \"Pink Guster\",\n    \"block.minecraft.banner.guster.purple\": \"Purple Guster\",\n    \"block.minecraft.banner.guster.red\": \"Red Guster\",\n    \"block.minecraft.banner.guster.white\": \"White Guster\",\n    \"block.minecraft.banner.guster.yellow\": \"Yellow Guster\",\n    \"block.minecraft.heavy_core\": \"Heavy Core\",\n    \"block.minecraft.vault\": \"Vault\",\n    \"chat.disabled.invalid_command_signature\": \"Command had unexpected or missing command argument signatures.\",\n    \"chat.disabled.invalid_signature\": \"Chat had an invalid signature. Please try reconnecting.\",\n    \"chat.disabled.out_of_order_chat\": \"Chat received out-of-order. Did your system time change?\",\n    \"chunk.toast.checkLog\": \"See log for more details\",\n    \"chunk.toast.loadFailure\": \"Failed to load chunk at %s\",\n    \"chunk.toast.lowDiskSpace\": \"Low disk space!\",\n    \"chunk.toast.lowDiskSpace.description\": \"Might not be able to save the world.\",\n    \"chunk.toast.saveFailure\": \"Failed to save chunk at %s\",\n    \"commands.datapack.disable.failed.feature\": \"Pack '%s' cannot be disabled, since it is part of an enabled flag!\",\n    \"commands.setworldspawn.failure.not_overworld\": \"Can only set the world spawn for overworld\",\n    \"commands.transfer.error.no_players\": \"Must specify at least one player to transfer\",\n    \"commands.transfer.success.multiple\": \"Transferring %s players to %s:%s\",\n    \"commands.transfer.success.single\": \"Transferring %s to %s:%s\",\n    \"connect.failed.transfer\": \"Connection failed while transferring to the server\",\n    \"connect.transferring\": \"Transferring to new server...\",\n    \"disconnect.packetError\": \"Network Protocol Error\",\n    \"disconnect.transfer\": \"Transferred to another server\",\n    \"effect.minecraft.infested\": \"Infested\",\n    \"effect.minecraft.oozing\": \"Oozing\",\n    \"effect.minecraft.raid_omen\": \"Raid Omen\",\n    \"effect.minecraft.trial_omen\": \"Trial Omen\",\n    \"effect.minecraft.weaving\": \"Weaving\",\n    \"effect.minecraft.wind_charged\": \"Wind Charged\",\n    \"enchantment.minecraft.breach\": \"Breach\",\n    \"enchantment.minecraft.density\": \"Density\",\n    \"enchantment.minecraft.wind_burst\": \"Wind Burst\",\n    \"entity.minecraft.armadillo\": \"Armadillo\",\n    \"entity.minecraft.bogged\": \"Bogged\",\n    \"entity.minecraft.ominous_item_spawner\": \"Ominous Item Spawner\",\n    \"filled_map.trial_chambers\": \"Trial Chambers Map\",\n    \"gamerule.spawnChunkRadius\": \"Spawn chunk radius\",\n    \"gamerule.spawnChunkRadius.description\": \"Amount of chunks that stay loaded around the overworld spawn position.\",\n    \"gamerule.spawnRadius.description\": \"Controls the size of the area around the spawn point that players can spawn in.\",\n    \"item.components\": \"%s component(s)\",\n    \"item.minecraft.armadillo_scute\": \"Armadillo Scute\",\n    \"item.minecraft.armadillo_spawn_egg\": \"Armadillo Spawn Egg\",\n    \"item.minecraft.bogged_spawn_egg\": \"Bogged Spawn Egg\",\n    \"item.minecraft.breeze_rod\": \"Breeze Rod\",\n    \"item.minecraft.flow_banner_pattern.desc\": \"Flow\",\n    \"item.minecraft.flow_pottery_sherd\": \"Flow Pottery Sherd\",\n    \"item.minecraft.guster_banner_pattern.desc\": \"Guster\",\n    \"item.minecraft.guster_pottery_sherd\": \"Guster Pottery Sherd\",\n    \"item.minecraft.lingering_potion.effect.infested\": \"Lingering Potion of Infestation\",\n    \"item.minecraft.lingering_potion.effect.oozing\": \"Lingering Potion of Oozing\",\n    \"item.minecraft.lingering_potion.effect.weaving\": \"Lingering Potion of Weaving\",\n    \"item.minecraft.lingering_potion.effect.wind_charged\": \"Lingering Potion of Wind Charging\",\n    \"item.minecraft.mace\": \"Mace\",\n    \"item.minecraft.ominous_bottle\": \"Ominous Bottle\",\n    \"item.minecraft.ominous_trial_key\": \"Ominous Trial Key\",\n    \"item.minecraft.potion.effect.infested\": \"Potion of Infestation\",\n    \"item.minecraft.potion.effect.oozing\": \"Potion of Oozing\",\n    \"item.minecraft.potion.effect.weaving\": \"Potion of Weaving\",\n    \"item.minecraft.potion.effect.wind_charged\": \"Potion of Wind Charging\",\n    \"item.minecraft.scrape_pottery_sherd\": \"Scrape Pottery Sherd\",\n    \"item.minecraft.splash_potion.effect.infested\": \"Splash Potion of Infestation\",\n    \"item.minecraft.splash_potion.effect.oozing\": \"Splash Potion of Oozing\",\n    \"item.minecraft.splash_potion.effect.weaving\": \"Splash Potion of Weaving\",\n    \"item.minecraft.splash_potion.effect.wind_charged\": \"Splash Potion of Wind Charging\",\n    \"item.minecraft.tipped_arrow.effect.infested\": \"Arrow of Infestation\",\n    \"item.minecraft.tipped_arrow.effect.oozing\": \"Arrow of Oozing\",\n    \"item.minecraft.tipped_arrow.effect.weaving\": \"Arrow of Weaving\",\n    \"item.minecraft.tipped_arrow.effect.wind_charged\": \"Arrow of Wind Charging\",\n    \"item.minecraft.turtle_scute\": \"Turtle Scute\",\n    \"item.minecraft.wolf_armor\": \"Wolf Armor\",\n    \"item.modifiers.body\": \"When equipped:\",\n    \"mco.backup.narration\": \"Backup from %s\",\n    \"mco.client.outdated.stable.version\": \"Your client version (%s) is not compatible with Realms.\\n\\nPlease use the most recent version of Minecraft.\",\n    \"mco.client.unsupported.snapshot.version\": \"Your client version (%s) is not compatible with Realms.\\n\\nRealms is not available for this snapshot version.\",\n    \"mco.invited.player.narration\": \"Invited player %s\",\n    \"multiplayer.disconnect.transfers_disabled\": \"Server does not accept transfers\",\n    \"optimizeWorld.stage.finished.chunks\": \"Finishing up upgrading chunks...\",\n    \"optimizeWorld.stage.finished.entities\": \"Finishing up upgrading entities...\",\n    \"optimizeWorld.stage.finished.poi\": \"Finishing up upgrading points of interest...\",\n    \"optimizeWorld.stage.upgrading.entities\": \"Upgrading all entities...\",\n    \"optimizeWorld.stage.upgrading.poi\": \"Upgrading all points of interest...\",\n    \"options.accessibility.menu_background_blurriness\": \"Menu Background Blur\",\n    \"options.accessibility.menu_background_blurriness.tooltip\": \"Changes the blurriness of menu backgrounds\",\n    \"options.font\": \"Font Settings...\",\n    \"options.font.title\": \"Font Settings\",\n    \"options.japaneseGlyphVariants\": \"Japanese Glyph Variants\",\n    \"options.japaneseGlyphVariants.tooltip\": \"Uses Japanese variants of CJK characters in the default font\",\n    \"options.telemetry.disabled\": \"Telemetry is disabled.\",\n    \"particle.invalidOptions\": \"Can't parse particle options: %s\",\n    \"selectWorld.allowCommands.new\": \"Allow Commands\",\n    \"selectWorld.commands\": \"Commands\",\n    \"selectWorld.warning.lowDiskSpace.description\": \"There is not much space left on your device.\\nRunning out of disk space while in game can lead to your world being damaged.\",\n    \"selectWorld.warning.lowDiskSpace.title\": \"Warning! Low disk space!\",\n    \"slot.only_single_allowed\": \"Only single slots allowed, got '%s'\",\n    \"subtitles.block.candle.extinguish\": \"Candle extinguishes\",\n    \"subtitles.block.trial_spawner.about_to_spawn_item\": \"Ominous item prepares\",\n    \"subtitles.block.trial_spawner.ambient_charged\": \"Ominous Trial Spawner crackles\",\n    \"subtitles.block.trial_spawner.charge_activate\": \"Omen engulfs Trial Spawner\",\n    \"subtitles.block.trial_spawner.spawn_item\": \"Ominous item drops\",\n    \"subtitles.block.trial_spawner.spawn_item_begin\": \"Ominous item appears\",\n    \"subtitles.block.vault.activate\": \"Vault ignites\",\n    \"subtitles.block.vault.ambient\": \"Vault crackles\",\n    \"subtitles.block.vault.close_shutter\": \"Vault closes\",\n    \"subtitles.block.vault.deactivate\": \"Vault extinguishes\",\n    \"subtitles.block.vault.eject_item\": \"Vault ejects item\",\n    \"subtitles.block.vault.insert_item\": \"Vault unlocks\",\n    \"subtitles.block.vault.insert_item_fail\": \"Vault fails unlocking\",\n    \"subtitles.block.vault.open_shutter\": \"Vault opens\",\n    \"subtitles.block.wet_sponge.dries\": \"Sponge dries\",\n    \"subtitles.entity.armadillo.ambient\": \"Armadillo grunts\",\n    \"subtitles.entity.armadillo.brush\": \"Scute is brushed off\",\n    \"subtitles.entity.armadillo.death\": \"Armadillo dies\",\n    \"subtitles.entity.armadillo.eat\": \"Armadillo eats\",\n    \"subtitles.entity.armadillo.hurt\": \"Armadillo hurts\",\n    \"subtitles.entity.armadillo.hurt_reduced\": \"Armadillo shields itself\",\n    \"subtitles.entity.armadillo.land\": \"Armadillo lands\",\n    \"subtitles.entity.armadillo.peek\": \"Armadillo peeks\",\n    \"subtitles.entity.armadillo.roll\": \"Armadillo rolls up\",\n    \"subtitles.entity.armadillo.scute_drop\": \"Armadillo sheds scute\",\n    \"subtitles.entity.armadillo.unroll_finish\": \"Armadillo unrolls\",\n    \"subtitles.entity.armadillo.unroll_start\": \"Armadillo peeks\",\n    \"subtitles.entity.bogged.ambient\": \"Bogged rattles\",\n    \"subtitles.entity.bogged.death\": \"Bogged dies\",\n    \"subtitles.entity.bogged.hurt\": \"Bogged hurts\",\n    \"subtitles.entity.breeze.charge\": \"Breeze charges\",\n    \"subtitles.entity.breeze.deflect\": \"Breeze deflects\",\n    \"subtitles.entity.breeze.whirl\": \"Breeze whirls\",\n    \"subtitles.entity.donkey.jump\": \"Donkey jumps\",\n    \"subtitles.entity.mule.jump\": \"Mule jumps\",\n    \"subtitles.entity.wind_charge.throw\": \"Wind Charge flies\",\n    \"subtitles.event.mob_effect.bad_omen\": \"Omen takes hold\",\n    \"subtitles.event.mob_effect.raid_omen\": \"Raid looms nearby\",\n    \"subtitles.event.mob_effect.trial_omen\": \"Ominous trial looms nearby\",\n    \"subtitles.item.armor.equip_wolf\": \"Wolf Armor is fastened\",\n    \"subtitles.item.armor.unequip_wolf\": \"Wolf Armor snips away\",\n    \"subtitles.item.mace.smash_air\": \"Mace smashes\",\n    \"subtitles.item.mace.smash_ground\": \"Mace smashes\",\n    \"subtitles.item.ominous_bottle.dispose\": \"Bottle breaks\",\n    \"subtitles.item.wolf_armor.break\": \"Wolf Armor breaks\",\n    \"subtitles.item.wolf_armor.crack\": \"Wolf Armor cracks\",\n    \"subtitles.item.wolf_armor.damage\": \"Wolf Armor takes damage\",\n    \"subtitles.item.wolf_armor.repair\": \"Wolf Armor is repaired\",\n    \"trim_pattern.minecraft.bolt\": \"Bolt Armor Trim\",\n    \"trim_pattern.minecraft.flow\": \"Flow Armor Trim\"\n  },\n  \"1.20.3\": {\n    \"accessibility.onboarding.accessibility.button\": \"Accessibility Settings...\",\n    \"argument.style.invalid\": \"Invalid style: %s\",\n    \"block.minecraft.chiseled_copper\": \"Chiseled Copper\",\n    \"block.minecraft.chiseled_tuff\": \"Chiseled Tuff\",\n    \"block.minecraft.chiseled_tuff_bricks\": \"Chiseled Tuff Bricks\",\n    \"block.minecraft.copper_bulb\": \"Copper Bulb\",\n    \"block.minecraft.copper_door\": \"Copper Door\",\n    \"block.minecraft.copper_grate\": \"Copper Grate\",\n    \"block.minecraft.copper_trapdoor\": \"Copper Trapdoor\",\n    \"block.minecraft.crafter\": \"Crafter\",\n    \"block.minecraft.exposed_chiseled_copper\": \"Exposed Chiseled Copper\",\n    \"block.minecraft.exposed_copper_bulb\": \"Exposed Copper Bulb\",\n    \"block.minecraft.exposed_copper_door\": \"Exposed Copper Door\",\n    \"block.minecraft.exposed_copper_grate\": \"Exposed Copper Grate\",\n    \"block.minecraft.exposed_copper_trapdoor\": \"Exposed Copper Trapdoor\",\n    \"block.minecraft.oxidized_chiseled_copper\": \"Oxidized Chiseled Copper\",\n    \"block.minecraft.oxidized_copper_bulb\": \"Oxidized Copper Bulb\",\n    \"block.minecraft.oxidized_copper_door\": \"Oxidized Copper Door\",\n    \"block.minecraft.oxidized_copper_grate\": \"Oxidized Copper Grate\",\n    \"block.minecraft.oxidized_copper_trapdoor\": \"Oxidized Copper Trapdoor\",\n    \"block.minecraft.polished_tuff\": \"Polished Tuff\",\n    \"block.minecraft.polished_tuff_slab\": \"Polished Tuff Slab\",\n    \"block.minecraft.polished_tuff_stairs\": \"Polished Tuff Stairs\",\n    \"block.minecraft.polished_tuff_wall\": \"Polished Tuff Wall\",\n    \"block.minecraft.short_grass\": \"Short Grass\",\n    \"block.minecraft.trial_spawner\": \"Trial Spawner\",\n    \"block.minecraft.tuff_brick_slab\": \"Tuff Brick Slab\",\n    \"block.minecraft.tuff_brick_stairs\": \"Tuff Brick Stairs\",\n    \"block.minecraft.tuff_brick_wall\": \"Tuff Brick Wall\",\n    \"block.minecraft.tuff_bricks\": \"Tuff Bricks\",\n    \"block.minecraft.tuff_slab\": \"Tuff Slab\",\n    \"block.minecraft.tuff_stairs\": \"Tuff Stairs\",\n    \"block.minecraft.tuff_wall\": \"Tuff Wall\",\n    \"block.minecraft.waxed_chiseled_copper\": \"Waxed Chiseled Copper\",\n    \"block.minecraft.waxed_copper_bulb\": \"Waxed Copper Bulb\",\n    \"block.minecraft.waxed_copper_door\": \"Waxed Copper Door\",\n    \"block.minecraft.waxed_copper_grate\": \"Waxed Copper Grate\",\n    \"block.minecraft.waxed_copper_trapdoor\": \"Waxed Copper Trapdoor\",\n    \"block.minecraft.waxed_exposed_chiseled_copper\": \"Waxed Exposed Chiseled Copper\",\n    \"block.minecraft.waxed_exposed_copper_bulb\": \"Waxed Exposed Copper Bulb\",\n    \"block.minecraft.waxed_exposed_copper_door\": \"Waxed Exposed Copper Door\",\n    \"block.minecraft.waxed_exposed_copper_grate\": \"Waxed Exposed Copper Grate\",\n    \"block.minecraft.waxed_exposed_copper_trapdoor\": \"Waxed Exposed Copper Trapdoor\",\n    \"block.minecraft.waxed_oxidized_chiseled_copper\": \"Waxed Oxidized Chiseled Copper\",\n    \"block.minecraft.waxed_oxidized_copper_bulb\": \"Waxed Oxidized Copper Bulb\",\n    \"block.minecraft.waxed_oxidized_copper_door\": \"Waxed Oxidized Copper Door\",\n    \"block.minecraft.waxed_oxidized_copper_grate\": \"Waxed Oxidized Copper Grate\",\n    \"block.minecraft.waxed_oxidized_copper_trapdoor\": \"Waxed Oxidized Copper Trapdoor\",\n    \"block.minecraft.waxed_weathered_chiseled_copper\": \"Waxed Weathered Chiseled Copper\",\n    \"block.minecraft.waxed_weathered_copper_bulb\": \"Waxed Weathered Copper Bulb\",\n    \"block.minecraft.waxed_weathered_copper_door\": \"Waxed Weathered Copper Door\",\n    \"block.minecraft.waxed_weathered_copper_grate\": \"Waxed Weathered Copper Grate\",\n    \"block.minecraft.waxed_weathered_copper_trapdoor\": \"Waxed Weathered Copper Trapdoor\",\n    \"block.minecraft.weathered_chiseled_copper\": \"Weathered Chiseled Copper\",\n    \"block.minecraft.weathered_copper_bulb\": \"Weathered Copper Bulb\",\n    \"block.minecraft.weathered_copper_door\": \"Weathered Copper Door\",\n    \"block.minecraft.weathered_copper_grate\": \"Weathered Copper Grate\",\n    \"block.minecraft.weathered_copper_trapdoor\": \"Weathered Copper Trapdoor\",\n    \"command.forkLimit\": \"Maximum number of contexts (%s) reached\",\n    \"commands.debug.function.noReturnRun\": \"Tracing can't be used with return run\",\n    \"commands.execute.function.instantiationFailure\": \"Failed to instantiate function %s: %s\",\n    \"commands.function.instantiationFailure\": \"Failed to instantiate function %s: %s\",\n    \"commands.function.result\": \"Function %s returned %s\",\n    \"commands.function.scheduled.multiple\": \"Running functions %s\",\n    \"commands.function.scheduled.no_functions\": \"Can't find any functions for name %s\",\n    \"commands.function.scheduled.single\": \"Running function %s\",\n    \"commands.kick.owner.failed\": \"Cannot kick server owner in LAN game\",\n    \"commands.kick.singleplayer.failed\": \"Cannot kick in an offline singleplayer game\",\n    \"commands.scoreboard.objectives.modify.displayAutoUpdate.disable\": \"Disabled display auto-update for objective %s\",\n    \"commands.scoreboard.objectives.modify.displayAutoUpdate.enable\": \"Enabled display auto-update for objective %s\",\n    \"commands.scoreboard.objectives.modify.objectiveFormat.clear\": \"Cleared default number format of objective %s\",\n    \"commands.scoreboard.objectives.modify.objectiveFormat.set\": \"Changed default number format of objective %s\",\n    \"commands.scoreboard.players.display.name.clear.success.multiple\": \"Cleared display name for %s entities in %s\",\n    \"commands.scoreboard.players.display.name.clear.success.single\": \"Cleared display name for %s in %s\",\n    \"commands.scoreboard.players.display.name.set.success.multiple\": \"Changed display name to %s for %s entities in %s\",\n    \"commands.scoreboard.players.display.name.set.success.single\": \"Changed display name to %s for %s in %s\",\n    \"commands.scoreboard.players.display.numberFormat.clear.success.multiple\": \"Cleared number format for %s entities in %s\",\n    \"commands.scoreboard.players.display.numberFormat.clear.success.single\": \"Cleared number format for %s in %s\",\n    \"commands.scoreboard.players.display.numberFormat.set.success.multiple\": \"Changed number format for %s entities in %s\",\n    \"commands.scoreboard.players.display.numberFormat.set.success.single\": \"Changed number format for %s in %s\",\n    \"commands.tick.query.percentiles\": \"Percentiles: P50: %sms P95: %sms P99: %sms, sample: %s\",\n    \"commands.tick.query.rate.running\": \"Target tick rate: %s per second.\\nAverage time per tick: %sms (Target: %sms)\",\n    \"commands.tick.query.rate.sprinting\": \"Target tick rate: %s per second (ignored, reference only).\\nAverage time per tick: %sms\",\n    \"commands.tick.rate.success\": \"Set the target tick rate to %s per second\",\n    \"commands.tick.sprint.report\": \"Sprint completed with %s ticks per second, or %s ms per tick\",\n    \"commands.tick.sprint.stop.fail\": \"No tick sprint in progress\",\n    \"commands.tick.sprint.stop.success\": \"A tick sprint interrupted\",\n    \"commands.tick.status.frozen\": \"The game is frozen\",\n    \"commands.tick.status.lagging\": \"The game is running, but can't keep up with the target tick rate\",\n    \"commands.tick.status.running\": \"The game runs normally\",\n    \"commands.tick.status.sprinting\": \"The game is sprinting\",\n    \"commands.tick.step.fail\": \"Unable to step the game - the game must be frozen first\",\n    \"commands.tick.step.stop.fail\": \"No tick step in progress\",\n    \"commands.tick.step.stop.success\": \"A tick step interrupted\",\n    \"commands.tick.step.success\": \"Stepping %s tick(s)\",\n    \"container.crafter\": \"Crafter\",\n    \"dataPack.update_1_21.description\": \"New features and content for Minecraft 1.21\",\n    \"dataPack.update_1_21.name\": \"Update 1.21\",\n    \"download.pack.failed\": \"%s out of %s packs failed to download\",\n    \"download.pack.progress.bytes\": \"Progress: %s (total size unknown)\",\n    \"download.pack.progress.percent\": \"Progress: %s%%\",\n    \"download.pack.title\": \"Downloading resource pack %s/%s\",\n    \"entity.minecraft.breeze\": \"Breeze\",\n    \"entity.minecraft.wind_charge\": \"Wind Charge\",\n    \"gamerule.maxCommandForkCount\": \"Command context limit\",\n    \"gamerule.maxCommandForkCount.description\": \"Maximum number of contexts that can be used by commands like 'execute as'.\",\n    \"gamerule.playersNetherPortalCreativeDelay\": \"Player's Nether portal delay in creative mode\",\n    \"gamerule.playersNetherPortalCreativeDelay.description\": \"Time (in ticks) that a creative mode player needs to stand in a Nether portal before changing dimensions.\",\n    \"gamerule.playersNetherPortalDefaultDelay\": \"Player's Nether portal delay in non-creative mode\",\n    \"gamerule.playersNetherPortalDefaultDelay.description\": \"Time (in ticks) that a non-creative mode player needs to stand in a Nether portal before changing dimensions.\",\n    \"gamerule.projectilesCanBreakBlocks\": \"Projectiles can break blocks\",\n    \"gamerule.projectilesCanBreakBlocks.description\": \"Controls whether impact projectiles will destroy blocks that are destructible by them.\",\n    \"gui.loadingMinecraft\": \"Loading Minecraft\",\n    \"gui.togglable_slot\": \"Click to disable slot\",\n    \"item.minecraft.breeze_spawn_egg\": \"Breeze Spawn Egg\",\n    \"item.minecraft.trial_key\": \"Trial Key\",\n    \"jigsaw_block.placement_priority\": \"Placement Priority:\",\n    \"jigsaw_block.placement_priority.tooltip\": \"When this Jigsaw block connects to a piece, this is the order in which that piece is processed for connections in the wider structure.\\n\\nPieces will be processed in descending priority with insertion order breaking ties.\",\n    \"jigsaw_block.selection_priority\": \"Selection Priority:\",\n    \"jigsaw_block.selection_priority.tooltip\": \"When the parent piece is being processed for connections, this is the order in which this Jigsaw block attempts to connect to its target piece.\\n\\nJigsaws will be processed in descending priority with random ordering breaking ties.\",\n    \"mco.account.privacy.info.button\": \"Read more about GDPR\",\n    \"mco.account.privacy.information\": \"Mojang implements certain procedures to help protect children and their privacy including complying with the Children�s Online Privacy Protection Act (COPPA) and General Data Protection Regulation (GDPR).\\n\\nYou may need to obtain parental consent before accessing your Realms account.\",\n    \"mco.compatibility.downgrade\": \"Downgrade\",\n    \"mco.compatibility.downgrade.description\": \"This world was last played in version %s; you are on version %s. Downgrading a world could cause corruption - we cannot guarantee that it will load or work.\\n\\nA backup of your world will be saved under \\\"World backups\\\". Please restore your world if needed.\",\n    \"mco.compatibility.unverifiable.message\": \"The version this world was last played in could not be verified. If the world gets upgraded or downgraded, a backup will be automatically created and saved under \\\"World backups\\\".\",\n    \"mco.compatibility.unverifiable.title\": \"Compatibility not verifiable\",\n    \"mco.compatibility.upgrade\": \"Upgrade\",\n    \"mco.compatibility.upgrade.description\": \"This world was last played in version %s; you are on version %s.\\n\\nA backup of your world will be saved under \\\"World backups\\\". Please restore your world if needed.\",\n    \"mco.compatibility.upgrade.title\": \"Do you really want to upgrade your world?\",\n    \"mco.notification.transferSubscription.buttonText\": \"Transfer Now\",\n    \"mco.notification.transferSubscription.message\": \"Java Realms subscriptions are moving to the Microsoft Store. Do not let your subscription expire!\\nTransfer now and get 30 days of Realms for free.\\nGo to Profile on minecraft.net to transfer your subscription.\",\n    \"mco.selectServer.minigameName\": \"Minigame: %s\",\n    \"mco.snapshot.createSnapshotPopup.text\": \"You are about to create a free Snapshot Realm that will be paired with your paid Realms subscription. This new Snapshot Realm will be accessible for as long as the paid subscription is active. Your paid Realm will not be affected.\",\n    \"mco.snapshot.createSnapshotPopup.title\": \"Create Snapshot Realm?\",\n    \"mco.snapshot.creating\": \"Creating Snapshot Realm...\",\n    \"mco.snapshot.description\": \"Paired with \\\"%s\\\"\",\n    \"mco.snapshot.friendsRealm.downgrade\": \"You need to be on version %s to join this Realm\",\n    \"mco.snapshot.friendsRealm.upgrade\": \"%s needs to upgrade their Realm before you can play from this version\",\n    \"mco.snapshot.paired\": \"This Snapshot Realm is paired with \\\"%s\\\"\",\n    \"mco.snapshot.parent.tooltip\": \"Use the latest release of Minecraft to play on this Realm\",\n    \"mco.snapshot.start\": \"Start free Snapshot Realm\",\n    \"mco.snapshot.subscription.info\": \"This is a Snapshot Realm that is paired to the subscription of your Realm '%s'. It will stay active for as long as its paired Realm is.\",\n    \"mco.snapshot.tooltip\": \"Use Snapshot Realms to get a sneak peek at upcoming versions of Minecraft, which might include new features and other changes.\\n\\nYou can find your normal Realms in the release version of the game.\",\n    \"mco.snapshotRealmsPopup.message\": \"Realms are now available in Snapshots starting with Snapshot 23w41a. Every Realms subscription comes with a free Snapshot Realm that is separate from your normal Java Realm!\",\n    \"mco.snapshotRealmsPopup.title\": \"Realms now available in Snapshots\",\n    \"mco.snapshotRealmsPopup.urlText\": \"Learn More\",\n    \"mco.version\": \"Version: %s\",\n    \"options.accessibility.narrator_hotkey.mac.tooltip\": \"Allows the Narrator to be toggled on and off with 'Cmd+B'\",\n    \"options.hideSplashTexts\": \"Hide Splash Texts\",\n    \"options.hideSplashTexts.tooltip\": \"Hides the yellow splash text in the main menu.\",\n    \"recover_world.bug_tracker\": \"Report a Bug\",\n    \"recover_world.button\": \"Attempt to Recover\",\n    \"recover_world.done.failed\": \"Failed to recover from previous state.\",\n    \"recover_world.done.success\": \"Recovery was successful!\",\n    \"recover_world.done.title\": \"Recovery done\",\n    \"recover_world.issue.missing_file\": \"Missing file\",\n    \"recover_world.issue.none\": \"No issues\",\n    \"recover_world.message\": \"The following issues occurred while trying to read world folder \\\"%s\\\".\\nIt might be possible to restore the world from an older state or you can report this issue on the bug tracker.\",\n    \"recover_world.no_fallback\": \"No state to recover from available\",\n    \"recover_world.restore\": \"Attempt to Restore\",\n    \"recover_world.restoring\": \"Attempting to restore world...\",\n    \"recover_world.state_entry\": \"State from %s: \",\n    \"recover_world.state_entry.unknown\": \"unknown\",\n    \"recover_world.title\": \"Failed to load world\",\n    \"recover_world.warning\": \"Failed to load world summary\",\n    \"selectWorld.incompatible.description\": \"This world cannot be opened in this version.\\nIt was last played in version %s.\",\n    \"selectWorld.incompatible.info\": \"Incompatible version: %s\",\n    \"selectWorld.incompatible.title\": \"Incompatible version\",\n    \"selectWorld.incompatible.tooltip\": \"This world cannot be opened because it was created by an incompatible version.\",\n    \"selectWorld.resource_load\": \"Preparing Resources...\",\n    \"subtitles.block.copper_bulb.turn_off\": \"Copper Bulb turns off\",\n    \"subtitles.block.copper_bulb.turn_on\": \"Copper Bulb turns on\",\n    \"subtitles.block.copper_trapdoor.close\": \"Trapdoor closes\",\n    \"subtitles.block.copper_trapdoor.open\": \"Trapdoor opens\",\n    \"subtitles.block.crafter.craft\": \"Crafter crafts\",\n    \"subtitles.block.crafter.fail\": \"Crafter fails crafting\",\n    \"subtitles.block.decorated_pot.insert\": \"Decorated Pot fills\",\n    \"subtitles.block.decorated_pot.insert_fail\": \"Decorated Pot wobbles\",\n    \"subtitles.block.hanging_sign.waxed_interact_fail\": \"Sign wobbles\",\n    \"subtitles.block.trial_spawner.ambient\": \"Trial Spawner crackles\",\n    \"subtitles.block.trial_spawner.close_shutter\": \"Trial Spawner closes\",\n    \"subtitles.block.trial_spawner.detect_player\": \"Trial Spawner charges up\",\n    \"subtitles.block.trial_spawner.eject_item\": \"Trial Spawner ejects items\",\n    \"subtitles.block.trial_spawner.open_shutter\": \"Trial Spawner opens\",\n    \"subtitles.block.trial_spawner.spawn_mob\": \"Mob spawns\",\n    \"subtitles.entity.breeze.death\": \"Breeze dies\",\n    \"subtitles.entity.breeze.hurt\": \"Breeze hurts\",\n    \"subtitles.entity.breeze.idle_air\": \"Breeze flies\",\n    \"subtitles.entity.breeze.idle_ground\": \"Breeze whirs\",\n    \"subtitles.entity.breeze.inhale\": \"Breeze inhales\",\n    \"subtitles.entity.breeze.jump\": \"Breeze jumps\",\n    \"subtitles.entity.breeze.land\": \"Breeze lands\",\n    \"subtitles.entity.breeze.shoot\": \"Breeze shoots\",\n    \"subtitles.entity.breeze.slide\": \"Breeze slides\",\n    \"subtitles.entity.generic.wind_burst\": \"Wind Charge bursts\",\n    \"subtitles.entity.parrot.imitate.breeze\": \"Parrot whirs\",\n    \"subtitles.entity.player.teleport\": \"Player teleports\",\n    \"symlink_warning.more_info\": \"More Information\",\n    \"telemetry_info.opt_in.description\": \"I consent to sending optional telemetry data\",\n    \"telemetry.event.optional.disabled\": \"%s (Optional) - Disabled\"\n  },\n  \"1.20\": {\n    \"advancements.adventure.craft_decorated_pot_using_only_sherds.description\": \"Make a Decorated Pot out of 4 Pottery Sherds\",\n    \"advancements.adventure.craft_decorated_pot_using_only_sherds.title\": \"Careful Restoration\",\n    \"advancements.adventure.read_power_from_chiseled_bookshelf.description\": \"Read the power signal of a Chiseled Bookshelf using a Comparator\",\n    \"advancements.adventure.read_power_from_chiseled_bookshelf.title\": \"The Power of Books\",\n    \"advancements.adventure.salvage_sherd.description\": \"Brush a Suspicious block to obtain a Pottery Sherd\",\n    \"advancements.adventure.salvage_sherd.title\": \"Respecting the Remnants\",\n    \"advancements.adventure.trim_with_all_exclusive_armor_patterns.description\": \"Apply these smithing templates at least once: Spire, Snout, Rib, Ward, Silence, Vex, Tide, Wayfinder\",\n    \"advancements.adventure.trim_with_all_exclusive_armor_patterns.title\": \"Smithing with Style\",\n    \"advancements.adventure.trim_with_any_armor_pattern.description\": \"Craft a trimmed armor at a Smithing Table\",\n    \"advancements.adventure.trim_with_any_armor_pattern.title\": \"Crafting a New Look\",\n    \"advancements.husbandry.feed_snifflet.description\": \"Feed a Snifflet\",\n    \"advancements.husbandry.feed_snifflet.title\": \"Little Sniffs\",\n    \"advancements.husbandry.obtain_sniffer_egg.description\": \"Obtain a Sniffer Egg\",\n    \"advancements.husbandry.obtain_sniffer_egg.title\": \"Smells Interesting\",\n    \"advancements.husbandry.plant_any_sniffer_seed.description\": \"Plant any Sniffer seed\",\n    \"advancements.husbandry.plant_any_sniffer_seed.title\": \"Planting the Past\",\n    \"block.minecraft.calibrated_sculk_sensor\": \"Calibrated Sculk Sensor\",\n    \"block.minecraft.pitcher_crop\": \"Pitcher Crop\",\n    \"block.minecraft.pitcher_plant\": \"Pitcher Plant\",\n    \"block.minecraft.sniffer_egg\": \"Sniffer Egg\",\n    \"block.minecraft.suspicious_gravel\": \"Suspicious Gravel\",\n    \"commands.data.modify.invalid_substring\": \"Invalid substring indices: %s to %s\",\n    \"commands.function.success.multiple.result\": \"Executed %s functions\",\n    \"commands.function.success.single.result\": \"Function '%2$s' returned %1$s\",\n    \"death.attack.genericKill\": \"%1$s was killed\",\n    \"death.attack.genericKill.player\": \"%1$s was killed whilst fighting %2$s\",\n    \"death.attack.outsideBorder\": \"%1$s left the confines of this world\",\n    \"death.attack.outsideBorder.player\": \"%1$s left the confines of this world whilst fighting %2$s\",\n    \"disconnect.ignoring_status_request\": \"Ignoring status request\",\n    \"gui.toRealms\": \"Back to Realms List\",\n    \"gui.toWorld\": \"Back to World List\",\n    \"item.minecraft.angler_pottery_shard\": \"Angler Pottery Shard\",\n    \"item.minecraft.angler_pottery_sherd\": \"Angler Pottery Sherd\",\n    \"item.minecraft.archer_pottery_shard\": \"Archer Pottery Shard\",\n    \"item.minecraft.archer_pottery_sherd\": \"Archer Pottery Sherd\",\n    \"item.minecraft.arms_up_pottery_shard\": \"Arms Up Pottery Shard\",\n    \"item.minecraft.arms_up_pottery_sherd\": \"Arms Up Pottery Sherd\",\n    \"item.minecraft.blade_pottery_shard\": \"Blade Pottery Shard\",\n    \"item.minecraft.blade_pottery_sherd\": \"Blade Pottery Sherd\",\n    \"item.minecraft.brewer_pottery_shard\": \"Brewer Pottery Shard\",\n    \"item.minecraft.brewer_pottery_sherd\": \"Brewer Pottery Sherd\",\n    \"item.minecraft.burn_pottery_shard\": \"Burn Pottery Shard\",\n    \"item.minecraft.burn_pottery_sherd\": \"Burn Pottery Sherd\",\n    \"item.minecraft.danger_pottery_shard\": \"Danger Pottery Shard\",\n    \"item.minecraft.danger_pottery_sherd\": \"Danger Pottery Sherd\",\n    \"item.minecraft.explorer_pottery_shard\": \"Explorer Pottery Shard\",\n    \"item.minecraft.explorer_pottery_sherd\": \"Explorer Pottery Sherd\",\n    \"item.minecraft.friend_pottery_shard\": \"Friend Pottery Shard\",\n    \"item.minecraft.friend_pottery_sherd\": \"Friend Pottery Sherd\",\n    \"item.minecraft.heart_pottery_shard\": \"Heart Pottery Shard\",\n    \"item.minecraft.heart_pottery_sherd\": \"Heart Pottery Sherd\",\n    \"item.minecraft.heartbreak_pottery_shard\": \"Heartbreak Pottery Shard\",\n    \"item.minecraft.heartbreak_pottery_sherd\": \"Heartbreak Pottery Sherd\",\n    \"item.minecraft.howl_pottery_shard\": \"Howl Pottery Shard\",\n    \"item.minecraft.howl_pottery_sherd\": \"Howl Pottery Sherd\",\n    \"item.minecraft.miner_pottery_shard\": \"Miner Pottery Shard\",\n    \"item.minecraft.miner_pottery_sherd\": \"Miner Pottery Sherd\",\n    \"item.minecraft.mourner_pottery_shard\": \"Mourner Pottery Shard\",\n    \"item.minecraft.mourner_pottery_sherd\": \"Mourner Pottery Sherd\",\n    \"item.minecraft.music_disc_relic\": \"Music Disc\",\n    \"item.minecraft.music_disc_relic.desc\": \"Aaron Cherof - Relic\",\n    \"item.minecraft.pitcher_plant\": \"Pitcher Plant\",\n    \"item.minecraft.pitcher_pod\": \"Pitcher Pod\",\n    \"item.minecraft.plenty_pottery_shard\": \"Plenty Pottery Shard\",\n    \"item.minecraft.plenty_pottery_sherd\": \"Plenty Pottery Sherd\",\n    \"item.minecraft.prize_pottery_shard\": \"Prize Pottery Shard\",\n    \"item.minecraft.prize_pottery_sherd\": \"Prize Pottery Sherd\",\n    \"item.minecraft.sheaf_pottery_shard\": \"Sheaf Pottery Shard\",\n    \"item.minecraft.sheaf_pottery_sherd\": \"Sheaf Pottery Sherd\",\n    \"item.minecraft.shelter_pottery_shard\": \"Shelter Pottery Shard\",\n    \"item.minecraft.shelter_pottery_sherd\": \"Shelter Pottery Sherd\",\n    \"item.minecraft.skull_pottery_shard\": \"Skull Pottery Shard\",\n    \"item.minecraft.skull_pottery_sherd\": \"Skull Pottery Sherd\",\n    \"item.minecraft.snort_pottery_shard\": \"Snort Pottery Shard\",\n    \"item.minecraft.snort_pottery_sherd\": \"Snort Pottery Sherd\",\n    \"mco.backup.entry\": \"Backup (%s)\",\n    \"mco.backup.entry.description\": \"Description\",\n    \"mco.backup.entry.enabledPack\": \"Enabled Pack\",\n    \"mco.backup.entry.gameDifficulty\": \"Game Difficulty\",\n    \"mco.backup.entry.gameMode\": \"Game Mode\",\n    \"mco.backup.entry.gameServerVersion\": \"Game Server Version\",\n    \"mco.backup.entry.name\": \"Name\",\n    \"mco.backup.entry.seed\": \"Seed\",\n    \"mco.backup.entry.templateName\": \"Template Name\",\n    \"mco.backup.entry.undefined\": \"Undefined Change\",\n    \"mco.backup.entry.uploaded\": \"Uploaded\",\n    \"mco.backup.entry.worldType\": \"World Type\",\n    \"mco.backup.info.title\": \"Changes from last backup\",\n    \"mco.backup.unknown\": \"UNKNOWN\",\n    \"mco.configure.world.invited.number\": \"Invited (%s)\",\n    \"mco.configure.world.minigame\": \"Current: %s\",\n    \"mco.configure.world.subscription.remaining.months.days\": \"%1$s month(s), %2$s day(s)\",\n    \"mco.configure.world.subscription.remaining.months\": \"%1$s month(s)\",\n    \"mco.configure.world.subscription.remaining.days\": \"%1$s day(s)\",\n    \"mco.configure.world.uninvite.player\": \"Are you sure that you want to uninvite '%s'?\",\n    \"mco.download.percent\": \"%s %%\",\n    \"mco.download.resourcePack.fail\": \"Failed to download resource pack!\",\n    \"mco.download.speed\": \"(%s/s)\",\n    \"mco.errorMessage.6005\": \"World locked\",\n    \"mco.errorMessage.6006\": \"World is out of date\",\n    \"mco.errorMessage.6007\": \"User in too many Realms\",\n    \"mco.errorMessage.6008\": \"Invalid Realm name\",\n    \"mco.errorMessage.6009\": \"Invalid Realm description\",\n    \"mco.errorMessage.generic\": \"An error occurred: \",\n    \"mco.errorMessage.realmsService\": \"An error occurred (%s):\",\n    \"mco.errorMessage.realmsService.realmsError\": \"Realms (%s):\",\n    \"mco.info\": \"Info!\",\n    \"mco.question\": \"Question\",\n    \"mco.upload.entry.id\": \"%1$s (%2$s)\",\n    \"mco.upload.entry.cheats\": \"%1$s, %2$s\",\n    \"mco.time.now\": \"right now\",\n    \"mco.time.secondsAgo\": \"%1$s second(s) ago\",\n    \"mco.time.minutesAgo\": \"%1$s minute(s) ago\",\n    \"mco.time.hoursAgo\": \"%1$s hour(s) ago\",\n    \"mco.time.daysAgo\": \"%1$s day(s) ago\",\n    \"mco.warning\": \"Warning!\",\n    \"mco.worldSlot.minigame\": \"Minigame\",\n    \"multiplayer.disconnect.invalid_public_key_signature.new\": \"Invalid signature for profile public key.\\nTry restarting your game.\",\n    \"quickplay.error.invalid_identifier\": \"Could not find world with the provided identifier\",\n    \"quickplay.error.realm_connect\": \"Could not connect to Realm\",\n    \"quickplay.error.realm_permission\": \"Lacking permission to connect to this Realm\",\n    \"quickplay.error.title\": \"Failed to Quick Play\",\n    \"subtitles.block.amethyst_block.resonate\": \"Amethyst resonates\",\n    \"subtitles.block.sign.waxed_interact_fail\": \"Sign wobbles\",\n    \"subtitles.block.sniffer_egg.crack\": \"Sniffer Egg cracks\",\n    \"subtitles.block.sniffer_egg.hatch\": \"Sniffer Egg hatches\",\n    \"subtitles.block.sniffer_egg.plop\": \"Sniffer plops\",\n    \"subtitles.entity.sniffer.egg_crack\": \"Sniffer Egg cracks\",\n    \"subtitles.entity.sniffer.egg_hatch\": \"Sniffer Egg hatches\",\n    \"subtitles.item.brush.brushing.generic\": \"Brushing\",\n    \"subtitles.item.brush.brushing.gravel\": \"Brushing Gravel\",\n    \"subtitles.item.brush.brushing.gravel.complete\": \"Brushing Gravel completed\",\n    \"subtitles.item.brush.brushing.sand\": \"Brushing Sand\",\n    \"subtitles.item.brush.brushing.sand.complete\": \"Brushing Sand completed\",\n    \"telemetry.event.advancement_made.description\": \"Understanding the context behind receiving an advancement can help us better understand and improve the progression of the game.\",\n    \"telemetry.event.advancement_made.title\": \"Advancement Made\",\n    \"telemetry.event.game_load_times.description\": \"This event can help us figure out where startup performance improvements are needed by measuring the execution times of the startup phases.\",\n    \"telemetry.event.game_load_times.title\": \"Game Load Times\",\n    \"telemetry.property.advancement_game_time.title\": \"Game Time (Ticks)\",\n    \"telemetry.property.advancement_id.title\": \"Advancement ID\",\n    \"telemetry.property.launcher_name.title\": \"Launcher Name\",\n    \"telemetry.property.load_time_bootstrap_ms.title\": \"Bootstrap Time (Milliseconds)\",\n    \"telemetry.property.load_time_loading_overlay_ms.title\": \"Time in Loading Screen (Milliseconds)\",\n    \"telemetry.property.load_time_pre_window_ms.title\": \"Time Before Window Opens (Milliseconds)\",\n    \"telemetry.property.load_time_total_time_ms.title\": \"Total Load Time (Milliseconds)\",\n    \"telemetry.property.realms_map_content.title\": \"Realms Map Content (Minigame Name)\",\n    \"trim_pattern.minecraft.host\": \"Host Armor Trim\",\n    \"trim_pattern.minecraft.raiser\": \"Raiser Armor Trim\",\n    \"trim_pattern.minecraft.shaper\": \"Shaper Armor Trim\",\n    \"trim_pattern.minecraft.silence\": \"Silence Armor Trim\",\n    \"trim_pattern.minecraft.wayfinder\": \"Wayfinder Armor Trim\"\n  },\n  \"1.19.4\": {\n    \"accessibility.onboarding.screen.narrator\": \"Press enter to enable the narrator\",\n    \"accessibility.onboarding.screen.title\": \"Welcome to Minecraft!\\n\\nWould you like to enable the Narrator or visit the Accessibility Settings?\",\n    \"argument.time.tick_count_too_low\": \"Tick count must not be less than %s, found %s\",\n    \"biome.minecraft.cherry_grove\": \"Cherry Grove\",\n    \"block.minecraft.cherry_button\": \"Cherry Button\",\n    \"block.minecraft.cherry_door\": \"Cherry Door\",\n    \"block.minecraft.cherry_fence\": \"Cherry Fence\",\n    \"block.minecraft.cherry_fence_gate\": \"Cherry Fence Gate\",\n    \"block.minecraft.cherry_hanging_sign\": \"Cherry Hanging Sign\",\n    \"block.minecraft.cherry_leaves\": \"Cherry Leaves\",\n    \"block.minecraft.cherry_log\": \"Cherry Log\",\n    \"block.minecraft.cherry_planks\": \"Cherry Planks\",\n    \"block.minecraft.cherry_pressure_plate\": \"Cherry Pressure Plate\",\n    \"block.minecraft.cherry_sapling\": \"Cherry Sapling\",\n    \"block.minecraft.cherry_sign\": \"Cherry Sign\",\n    \"block.minecraft.cherry_slab\": \"Cherry Slab\",\n    \"block.minecraft.cherry_stairs\": \"Cherry Stairs\",\n    \"block.minecraft.cherry_trapdoor\": \"Cherry Trapdoor\",\n    \"block.minecraft.cherry_wall_hanging_sign\": \"Cherry Wall Hanging Sign\",\n    \"block.minecraft.cherry_wall_sign\": \"Cherry Wall Sign\",\n    \"block.minecraft.cherry_wood\": \"Cherry Wood\",\n    \"block.minecraft.decorated_pot\": \"Decorated Pot\",\n    \"block.minecraft.pink_petals\": \"Pink Petals\",\n    \"block.minecraft.potted_cherry_sapling\": \"Potted Cherry Sapling\",\n    \"block.minecraft.potted_torchflower\": \"Potted Torchflower\",\n    \"block.minecraft.stripped_cherry_log\": \"Stripped Cherry Log\",\n    \"block.minecraft.stripped_cherry_wood\": \"Stripped Cherry Wood\",\n    \"block.minecraft.suspicious_sand\": \"Suspicious Sand\",\n    \"block.minecraft.torchflower\": \"Torchflower\",\n    \"block.minecraft.torchflower_crop\": \"Torchflower Crop\",\n    \"commands.damage.invulnerable\": \"Target is invulnerable to the given damage type\",\n    \"commands.damage.success\": \"Applied %s damage to %s\",\n    \"commands.data.modify.expected_value\": \"Expected value, got: %s\",\n    \"commands.ride.already_riding\": \"%s is already riding %s\",\n    \"commands.ride.dismount.success\": \"%s stopped riding %s\",\n    \"commands.ride.mount.failure.cant_ride_players\": \"Players can't be ridden\",\n    \"commands.ride.mount.failure.generic\": \"%s couldn't start riding %s\",\n    \"commands.ride.mount.failure.loop\": \"Can't mount entity on itself or any of its passengers\",\n    \"commands.ride.mount.failure.wrong_dimension\": \"Can't mount entity in different dimension\",\n    \"commands.ride.mount.success\": \"%s started riding %s\",\n    \"commands.ride.not_riding\": \"%s is not riding any vehicle\",\n    \"container.upgrade.error_tooltip\": \"Your item cannot be upgraded in this way\",\n    \"container.upgrade.missing_template_tooltip\": \"Put a Smithing Template here\",\n    \"controls.keybinds.duplicateKeybinds\": \"This key is also used for:\\n%s\",\n    \"createWorld.tab.game.title\": \"Game\",\n    \"createWorld.tab.more.title\": \"More\",\n    \"createWorld.tab.world.title\": \"World\",\n    \"credits_and_attribution.button.attribution\": \"Attribution\",\n    \"credits_and_attribution.button.credits\": \"Credits\",\n    \"credits_and_attribution.button.licenses\": \"Licenses\",\n    \"credits_and_attribution.screen.title\": \"Credits and Attribution\",\n    \"dataPack.bundle.name\": \"Bundles\",\n    \"dataPack.update_1_20.name\": \"Update 1.20\",\n    \"datapackFailure.safeMode.failed.title\": \"Failed to load world in Safe Mode.\",\n    \"datapackFailure.safeMode.failed.description\": \"This world contains invalid or corrupted save data.\",\n    \"debug.dump_dynamic_textures\": \"Saved dynamic textures to %s\",\n    \"debug.dump_dynamic_textures.help\": \"F3 + S = Dump dynamic textures\",\n    \"effect.duration.infinite\": \"?\",\n    \"entity.minecraft.block_display\": \"Block Display\",\n    \"entity.minecraft.falling_block_type\": \"Falling %s\",\n    \"entity.minecraft.interaction\": \"Interaction\",\n    \"entity.minecraft.item_display\": \"Item Display\",\n    \"entity.minecraft.sniffer\": \"Sniffer\",\n    \"entity.minecraft.text_display\": \"Text Display\",\n    \"gamerule.commandModificationBlockLimit\": \"Command Modification Block Limit\",\n    \"gamerule.commandModificationBlockLimit.description\": \"Number of blocks that can be changed at once by one command, such as fill or clone.\",\n    \"gamerule.doVinesSpread\": \"Vines spread\",\n    \"gamerule.doVinesSpread.description\": \"Controls whether or not the Vines block spreads randomly to adjacent blocks. Does not affect other type of vine blocks such as Weeping Vines, Twisting Vines, etc.\",\n    \"gui.banned.reason.defamation_impersonation_false_information\": \"Impersonation or sharing information to exploit or mislead others\",\n    \"gui.banned.reason.drugs\": \"References to illegal drugs\",\n    \"gui.banned.reason.extreme_violence_or_gore\": \"Depictions of real-life excessive violence or gore\",\n    \"gui.banned.reason.false_reporting\": \"Excessive false or inaccurate reports\",\n    \"gui.banned.reason.fraud\": \"Fraudulent acquisition or use of content\",\n    \"gui.banned.reason.generic_violation\": \"Violating Community Standards\",\n    \"gui.banned.reason.harassment_or_bullying\": \"Abusive language used in a directed, harmful manner\",\n    \"gui.banned.reason.hate_speech\": \"Hate speech or discrimination\",\n    \"gui.banned.reason.hate_terrorism_notorious_figure\": \"References to hate groups, terrorist organizations, or notorious figures\",\n    \"gui.banned.reason.imminent_harm_to_person_or_property\": \"Intent to cause real-life harm to persons or property\",\n    \"gui.banned.reason.nudity_or_pornography\": \"Displaying lewd or pornographic material\",\n    \"gui.banned.reason.sexually_inappropriate\": \"Topics or content of a sexual nature\",\n    \"gui.banned.reason.spam_or_advertising\": \"Spam or advertising\",\n    \"gui.continue\": \"Continue\",\n    \"gui.narrate.tab\": \"%s tab\",\n    \"item.minecraft.brush\": \"Brush\",\n    \"item.minecraft.cherry_boat\": \"Cherry Boat\",\n    \"item.minecraft.cherry_chest_boat\": \"Cherry Boat with Chest\",\n    \"item.minecraft.pottery_shard_archer\": \"Archer Pottery Shard\",\n    \"item.minecraft.pottery_shard_arms_up\": \"Arms Up Pottery Shard\",\n    \"item.minecraft.pottery_shard_prize\": \"Prize Pottery Shard\",\n    \"item.minecraft.pottery_shard_skull\": \"Skull Pottery Shard\",\n    \"item.minecraft.smithing_template\": \"Smithing Template\",\n    \"item.minecraft.smithing_template.applies_to\": \"Applies to:\",\n    \"item.minecraft.smithing_template.armor_trim.additions_slot_description\": \"Put an ingot or crystal here\",\n    \"item.minecraft.smithing_template.armor_trim.applies_to\": \"Armor\",\n    \"item.minecraft.smithing_template.armor_trim.base_slot_description\": \"Put a piece of armor here\",\n    \"item.minecraft.smithing_template.armor_trim.ingredients\": \"Ingots & Crystals\",\n    \"item.minecraft.smithing_template.ingredients\": \"Ingredients:\",\n    \"item.minecraft.smithing_template.netherite_upgrade.additions_slot_description\": \"Put a Netherite Ingot here\",\n    \"item.minecraft.smithing_template.netherite_upgrade.applies_to\": \"Diamond Equipment\",\n    \"item.minecraft.smithing_template.netherite_upgrade.base_slot_description\": \"Put a piece of Diamond armor, weapon or tool here\",\n    \"item.minecraft.smithing_template.netherite_upgrade.ingredients\": \"Netherite Ingot\",\n    \"item.minecraft.smithing_template.upgrade\": \"Upgrade: \",\n    \"item.minecraft.sniffer_spawn_egg\": \"Sniffer Spawn Egg\",\n    \"item.minecraft.torchflower_seeds\": \"Torchflower Seeds\",\n    \"mco.notification.dismiss\": \"Dismiss\",\n    \"mco.notification.visitUrl.message.default\": \"Please visit the link below\",\n    \"mco.notification.visitUrl.buttonText.default\": \"Open link\",\n    \"multiplayer.lan.server_found\": \"New server found: %s\",\n    \"multiplayer.player.list.narration\": \"Online players: %s\",\n    \"multiplayer.status.motd.narration\": \"Message of the day: %s\",\n    \"multiplayer.status.online\": \"Online\",\n    \"multiplayer.status.ping.narration\": \"Ping %s milliseconds\",\n    \"multiplayer.status.player_count.narration\": \"%s out of %s players online\",\n    \"multiplayer.status.version.narration\": \"Server version: %s\",\n    \"narration.tab_navigation.usage\": \"Press Ctrl and Tab to switch between tabs\",\n    \"narrator.position.tab\": \"Selected tab %s out of %s\",\n    \"narrator.ready_to_play\": \"Ready to play\",\n    \"options.accessibility.high_contrast\": \"High Contrast\",\n    \"options.accessibility.high_contrast.tooltip\": \"Enhances the contrast of UI elements\",\n    \"options.accessibility.high_contrast.error.tooltip\": \"High Contrast resource pack is not available\",\n    \"options.credits_and_attribution\": \"Credits & Attribution...\",\n    \"options.damageTiltStrength\": \"Damage Tilt\",\n    \"options.damageTiltStrength.tooltip\": \"The amount of camera shake caused by being hurt.\",\n    \"options.difficulty.easy.info\": \"Hostile mobs spawn but deal less damage. Hunger bar depletes and drains health down to 5 hearts.\",\n    \"options.difficulty.hard.info\": \"Hostile mobs spawn and deal more damage. Hunger bar depletes and drains all health.\",\n    \"options.difficulty.normal.info\": \"Hostile mobs spawn and deal standard damage. Hunger bar depletes and drains health down to half a heart.\",\n    \"options.difficulty.peaceful.info\": \"No hostile mobs and only some neutral mobs spawn. Hunger bar doesn't deplete and health replenishes over time.\",\n    \"options.glintSpeed\": \"Glint Speed\",\n    \"options.glintSpeed.tooltip\": \"Controls how fast the visual glint shimmers across enchanted items.\",\n    \"options.glintStrength\": \"Glint Strength\",\n    \"options.glintStrength.tooltip\": \"Controls how transparent the visual glint is on enchanted items.\",\n    \"options.multiplier\": \"%sx\",\n    \"options.notifications.display_time\": \"Notification Time\",\n    \"options.notifications.display_time.tooltip\": \"Affects the length of time that all notifications stay visible on the screen.\",\n    \"painting.dimensions\": \"%sx%s\",\n    \"painting.minecraft.alban.author\": \"Kristoffer Zetterstrand\",\n    \"painting.minecraft.alban.title\": \"Albanian\",\n    \"painting.minecraft.aztec.author\": \"Kristoffer Zetterstrand\",\n    \"painting.minecraft.aztec.title\": \"de_aztec\",\n    \"painting.minecraft.aztec2.author\": \"Kristoffer Zetterstrand\",\n    \"painting.minecraft.aztec2.title\": \"de_aztec\",\n    \"painting.minecraft.bomb.author\": \"Kristoffer Zetterstrand\",\n    \"painting.minecraft.bomb.title\": \"Target Successfully Bombed\",\n    \"painting.minecraft.burning_skull.author\": \"Kristoffer Zetterstrand\",\n    \"painting.minecraft.burning_skull.title\": \"Skull On Fire\",\n    \"painting.minecraft.bust.author\": \"Kristoffer Zetterstrand\",\n    \"painting.minecraft.bust.title\": \"Bust\",\n    \"painting.minecraft.courbet.author\": \"Kristoffer Zetterstrand\",\n    \"painting.minecraft.courbet.title\": \"Bonjour Monsieur Courbet\",\n    \"painting.minecraft.creebet.author\": \"Kristoffer Zetterstrand\",\n    \"painting.minecraft.creebet.title\": \"Creebet\",\n    \"painting.minecraft.donkey_kong.author\": \"Kristoffer Zetterstrand\",\n    \"painting.minecraft.donkey_kong.title\": \"Kong\",\n    \"painting.minecraft.earth.author\": \"Mojang\",\n    \"painting.minecraft.earth.title\": \"Earth\",\n    \"painting.minecraft.fighters.author\": \"Kristoffer Zetterstrand\",\n    \"painting.minecraft.fighters.title\": \"Fighters\",\n    \"painting.minecraft.fire.author\": \"Mojang\",\n    \"painting.minecraft.fire.title\": \"Fire\",\n    \"painting.minecraft.graham.author\": \"Kristoffer Zetterstrand\",\n    \"painting.minecraft.graham.title\": \"Graham\",\n    \"painting.minecraft.kebab.author\": \"Kristoffer Zetterstrand\",\n    \"painting.minecraft.kebab.title\": \"Kebab med tre pepperoni\",\n    \"painting.minecraft.match.author\": \"Kristoffer Zetterstrand\",\n    \"painting.minecraft.match.title\": \"Match\",\n    \"painting.minecraft.pigscene.author\": \"Kristoffer Zetterstrand\",\n    \"painting.minecraft.pigscene.title\": \"Pigscene\",\n    \"painting.minecraft.plant.author\": \"Kristoffer Zetterstrand\",\n    \"painting.minecraft.plant.title\": \"Paradistr�d\",\n    \"painting.minecraft.pointer.author\": \"Kristoffer Zetterstrand\",\n    \"painting.minecraft.pointer.title\": \"Pointer\",\n    \"painting.minecraft.pool.author\": \"Kristoffer Zetterstrand\",\n    \"painting.minecraft.pool.title\": \"The Pool\",\n    \"painting.minecraft.sea.author\": \"Kristoffer Zetterstrand\",\n    \"painting.minecraft.sea.title\": \"Seaside\",\n    \"painting.minecraft.skeleton.author\": \"Kristoffer Zetterstrand\",\n    \"painting.minecraft.skeleton.title\": \"Mortal Coil\",\n    \"painting.minecraft.skull_and_roses.author\": \"Kristoffer Zetterstrand\",\n    \"painting.minecraft.skull_and_roses.title\": \"Skull and Roses\",\n    \"painting.minecraft.stage.author\": \"Kristoffer Zetterstrand\",\n    \"painting.minecraft.stage.title\": \"The Stage Is Set\",\n    \"painting.minecraft.sunset.author\": \"Kristoffer Zetterstrand\",\n    \"painting.minecraft.sunset.title\": \"sunset_dense\",\n    \"painting.minecraft.void.author\": \"Kristoffer Zetterstrand\",\n    \"painting.minecraft.void.title\": \"The void\",\n    \"painting.minecraft.wanderer.author\": \"Kristoffer Zetterstrand\",\n    \"painting.minecraft.wanderer.title\": \"Wanderer\",\n    \"painting.minecraft.wasteland.author\": \"Kristoffer Zetterstrand\",\n    \"painting.minecraft.wasteland.title\": \"Wasteland\",\n    \"painting.minecraft.water.author\": \"Mojang\",\n    \"painting.minecraft.water.title\": \"Water\",\n    \"painting.minecraft.wind.author\": \"Mojang\",\n    \"painting.minecraft.wind.title\": \"Wind\",\n    \"painting.minecraft.wither.author\": \"Mojang\",\n    \"painting.minecraft.wither.title\": \"Wither\",\n    \"painting.random\": \"Random variant\",\n    \"resourcePack.high_contrast.name\": \"High Contrast\",\n    \"selectWorld.experiments\": \"Experiments\",\n    \"selectWorld.experiments.info\": \"Experiments are potential new features. Be careful as things might break. Experiments can't be turned off after world creation.\",\n    \"selectWorld.gameMode.adventure.info\": \"Same as Survival Mode, but blocks can't be added or removed.\",\n    \"selectWorld.gameMode.creative.info\": \"Create, build, and explore without limits. You can fly, have endless materials, and can't be hurt by monsters.\",\n    \"selectWorld.gameMode.hardcore.info\": \"Survival Mode locked to 'Hard' difficulty. You can't respawn if you die.\",\n    \"selectWorld.gameMode.spectator.info\": \"You can look but don't touch.\",\n    \"selectWorld.gameMode.survival.info\": \"Explore a mysterious world where you build, collect, craft, and fight monsters.\",\n    \"selectWorld.targetFolder\": \"Save folder: %s\",\n    \"subtitles.block.decorated_pot.shatter\": \"Pot shatters\",\n    \"subtitles.entity.sniffer.death\": \"Sniffer dies\",\n    \"subtitles.entity.sniffer.digging\": \"Sniffer digs\",\n    \"subtitles.entity.sniffer.digging_stop\": \"Sniffer stands up\",\n    \"subtitles.entity.sniffer.drop_seed\": \"Sniffer drops seed\",\n    \"subtitles.entity.sniffer.eat\": \"Sniffer eats\",\n    \"subtitles.entity.sniffer.happy\": \"Sniffer delights\",\n    \"subtitles.entity.sniffer.hurt\": \"Sniffer hurts\",\n    \"subtitles.entity.sniffer.idle\": \"Sniffer grunts\",\n    \"subtitles.entity.sniffer.scenting\": \"Sniffer scents\",\n    \"subtitles.entity.sniffer.searching\": \"Sniffer searches\",\n    \"subtitles.entity.sniffer.sniffing\": \"Sniffer sniffs\",\n    \"subtitles.entity.sniffer.step\": \"Sniffer steps\",\n    \"subtitles.item.brush.brush_sand_completed\": \"Brushing sand completed\",\n    \"subtitles.item.brush.brushing\": \"Brushing\",\n    \"trim_material.minecraft.amethyst\": \"Amethyst Material\",\n    \"trim_material.minecraft.copper\": \"Copper Material\",\n    \"trim_material.minecraft.diamond\": \"Diamond Material\",\n    \"trim_material.minecraft.emerald\": \"Emerald Material\",\n    \"trim_material.minecraft.gold\": \"Gold Material\",\n    \"trim_material.minecraft.iron\": \"Iron Material\",\n    \"trim_material.minecraft.lapis\": \"Lapis Material\",\n    \"trim_material.minecraft.netherite\": \"Netherite Material\",\n    \"trim_material.minecraft.quartz\": \"Quartz Material\",\n    \"trim_material.minecraft.redstone\": \"Redstone Material\",\n    \"trim_pattern.minecraft.coast\": \"Coast Armor Trim\",\n    \"trim_pattern.minecraft.dune\": \"Dune Armor Trim\",\n    \"trim_pattern.minecraft.eye\": \"Eye Armor Trim\",\n    \"trim_pattern.minecraft.rib\": \"Rib Armor Trim\",\n    \"trim_pattern.minecraft.sentry\": \"Sentry Armor Trim\",\n    \"trim_pattern.minecraft.snout\": \"Snout Armor Trim\",\n    \"trim_pattern.minecraft.spire\": \"Spire Armor Trim\",\n    \"trim_pattern.minecraft.tide\": \"Tide Armor Trim\",\n    \"trim_pattern.minecraft.vex\": \"Vex Armor Trim\",\n    \"trim_pattern.minecraft.ward\": \"Ward Armor Trim\",\n    \"trim_pattern.minecraft.wild\": \"Wild Armor Trim\",\n    \"upgrade.minecraft.netherite_upgrade\": \"Netherite Upgrade\"\n  },\n  \"1.19.3\": {\n    \"gui.chatReport.discard.draft\": \"Save as Draft\",\n    \"gui.chatReport.draft.title\": \"Edit draft chat report?\",\n    \"gui.chatReport.draft.content\": \"Would you like to continue editing the existing report or discard it and create a new one?\",\n    \"gui.chatReport.draft.quittotitle.title\": \"You have a draft chat report that will be lost if you quit\",\n    \"gui.chatReport.draft.quittotitle.content\": \"Would you like to continue editing it or discard it?\",\n    \"gui.chatReport.draft.discard\": \"Discard\",\n    \"gui.chatReport.draft.edit\": \"Continue Editing\",\n    \"gui.chatSelection.join\": \"%s joined the chat\",\n    \"selectWorld.experimental\": \"Experimental\",\n    \"selectWorld.warning.experimental.title\": \"Warning! These settings are using experimental features\",\n    \"selectWorld.warning.experimental.question\": \"These settings are experimental and could one day stop working. Do you wish to proceed?\",\n    \"selectWorld.warning.deprecated.title\": \"Warning! These settings are using deprecated features\",\n    \"selectWorld.warning.deprecated.question\": \"Some features used are deprecated and will stop working in the future. Do you wish to proceed?\",\n    \"selectWorld.experimental.title\": \"Experimental Features Warning\",\n    \"selectWorld.experimental.message\": \"Be careful!\\nSome of the selected packs require features that are still under development. Your world might crash, break or not work with future updates.\",\n    \"selectWorld.experimental.details\": \"Details\",\n    \"selectWorld.experimental.details.title\": \"Experimental feature requirements\",\n    \"selectWorld.experimental.details.entry\": \"Required experimental features: %s\",\n    \"lanServer.port\": \"Port Number\",\n    \"lanServer.port.unavailable\": \"Port not available.\\nLeave the edit box empty or enter a different number between 1024 and 65535.\",\n    \"lanServer.port.unavailable.new\": \"Port not available.\\nLeave the edit box empty or enter a different number between %s and %s.\",\n    \"lanServer.port.invalid\": \"Not a valid port.\\nLeave the edit box empty or enter a number between 1024 and 65535.\",\n    \"lanServer.port.invalid.new\": \"Not a valid port.\\nLeave the edit box empty or enter a number between %s and %s.\",\n    \"multiplayer.disconnect.expired_public_key\": \"Expired profile public key. Check that your system time is synchronized, and try restarting your game.\",\n    \"chat.disabled.expiredProfileKey\": \"Chat disabled due to expired profile public key. Please try reconnecting.\",\n    \"chat.disabled.chain_broken\": \"Chat disabled due to broken chain. Please try reconnecting.\",\n    \"chat.disabled.missingProfileKey\": \"Chat disabled due to missing profile public key. Please try reconnecting.\",\n    \"chat.tag.system\": \"Server message. Cannot be reported.\",\n    \"chat.tag.system_single_player\": \"Server message.\",\n    \"chat.filtered\": \"Filtered by the server.\",\n    \"chat.deleted_marker\": \"This chat message has been deleted by the server.\",\n    \"options.accessibility.panorama_speed\": \"Panorama Scroll Speed\",\n    \"options.telemetry\": \"Telemetry Data\",\n    \"options.telemetry.button\": \"Data Collection\",\n    \"options.telemetry.state.none\": \"None\",\n    \"options.telemetry.state.minimal\": \"Minimal\",\n    \"options.telemetry.state.all\": \"All\",\n    \"options.telemetry.button.tooltip\": \"\\\"%s\\\" includes only the required data.\\n\\\"%s\\\" includes optional, as well as the required data.\",\n    \"options.operatorItemsTab\": \"Operator Items Tab\",\n    \"resourcePack.programmer_art.name\": \"Programmer Art\",\n    \"resourcePack.vanilla.name\": \"Default\",\n    \"dataPack.vanilla.name\": \"Default\",\n    \"dataPack.bundle.description\": \"Enables experimental Bundle item\",\n    \"dataPack.update_1_20.description\": \"New features and content for Minecraft 1.20\",\n    \"hanging_sign.edit\": \"Edit Hanging Sign Message\",\n    \"block.minecraft.bamboo_planks\": \"Bamboo Planks\",\n    \"block.minecraft.bamboo_mosaic\": \"Bamboo Mosaic\",\n    \"block.minecraft.bamboo_door\": \"Bamboo Door\",\n    \"block.minecraft.bamboo_block\": \"Block of Bamboo\",\n    \"block.minecraft.stripped_bamboo_block\": \"Block of Stripped Bamboo\",\n    \"block.minecraft.bamboo_slab\": \"Bamboo Slab\",\n    \"block.minecraft.bamboo_mosaic_slab\": \"Bamboo Mosaic Slab\",\n    \"block.minecraft.chiseled_bookshelf\": \"Chiseled Bookshelf\",\n    \"block.minecraft.spawner.desc1\": \"Interact with Spawn Egg:\",\n    \"block.minecraft.spawner.desc2\": \"Sets Mob Type\",\n    \"block.minecraft.bamboo_stairs\": \"Bamboo Stairs\",\n    \"block.minecraft.bamboo_mosaic_stairs\": \"Bamboo Mosaic Stairs\",\n    \"block.minecraft.bamboo_sign\": \"Bamboo Sign\",\n    \"block.minecraft.bamboo_wall_sign\": \"Bamboo Wall Sign\",\n    \"block.minecraft.oak_hanging_sign\": \"Oak Hanging Sign\",\n    \"block.minecraft.spruce_hanging_sign\": \"Spruce Hanging Sign\",\n    \"block.minecraft.birch_hanging_sign\": \"Birch Hanging Sign\",\n    \"block.minecraft.acacia_hanging_sign\": \"Acacia Hanging Sign\",\n    \"block.minecraft.jungle_hanging_sign\": \"Jungle Hanging Sign\",\n    \"block.minecraft.crimson_hanging_sign\": \"Crimson Hanging Sign\",\n    \"block.minecraft.warped_hanging_sign\": \"Warped Hanging Sign\",\n    \"block.minecraft.dark_oak_hanging_sign\": \"Dark Oak Hanging Sign\",\n    \"block.minecraft.mangrove_hanging_sign\": \"Mangrove Hanging Sign\",\n    \"block.minecraft.bamboo_hanging_sign\": \"Bamboo Hanging Sign\",\n    \"block.minecraft.oak_wall_hanging_sign\": \"Oak Wall Hanging Sign\",\n    \"block.minecraft.spruce_wall_hanging_sign\": \"Spruce Wall Hanging Sign\",\n    \"block.minecraft.birch_wall_hanging_sign\": \"Birch Wall Hanging Sign\",\n    \"block.minecraft.acacia_wall_hanging_sign\": \"Acacia Wall Hanging Sign\",\n    \"block.minecraft.jungle_wall_hanging_sign\": \"Jungle Wall Hanging Sign\",\n    \"block.minecraft.dark_oak_wall_hanging_sign\": \"Dark Oak Wall Hanging Sign\",\n    \"block.minecraft.mangrove_wall_hanging_sign\": \"Mangrove Wall Hanging Sign\",\n    \"block.minecraft.crimson_wall_hanging_sign\": \"Crimson Wall Hanging Sign\",\n    \"block.minecraft.warped_wall_hanging_sign\": \"Warped Wall Hanging Sign\",\n    \"block.minecraft.bamboo_wall_hanging_sign\": \"Bamboo Wall Hanging Sign\",\n    \"block.minecraft.bamboo_pressure_plate\": \"Bamboo Pressure Plate\",\n    \"block.minecraft.bamboo_button\": \"Bamboo Button\",\n    \"block.minecraft.bamboo_fence\": \"Bamboo Fence\",\n    \"block.minecraft.bamboo_fence_gate\": \"Bamboo Fence Gate\",\n    \"block.minecraft.bamboo_trapdoor\": \"Bamboo Trapdoor\",\n    \"block.minecraft.piglin_wall_head\": \"Piglin Wall Head\",\n    \"block.minecraft.piglin_head\": \"Piglin Head\",\n    \"item.minecraft.bamboo_raft\": \"Bamboo Raft\",\n    \"item.minecraft.bamboo_chest_raft\": \"Bamboo Raft with Chest\",\n    \"item.minecraft.camel_spawn_egg\": \"Camel Spawn Egg\",\n    \"item.minecraft.ender_dragon_spawn_egg\": \"Ender Dragon Spawn Egg\",\n    \"item.minecraft.iron_golem_spawn_egg\": \"Iron Golem Spawn Egg\",\n    \"item.minecraft.snow_golem_spawn_egg\": \"Snow Golem Spawn Egg\",\n    \"item.minecraft.wither_spawn_egg\": \"Wither Spawn Egg\",\n    \"item.disabled\": \"Disabled item\",\n    \"entity.minecraft.camel\": \"Camel\",\n    \"itemGroup.coloredBlocks\": \"Colored Blocks\",\n    \"itemGroup.natural\": \"Natural Blocks\",\n    \"itemGroup.functional\": \"Functional Blocks\",\n    \"itemGroup.op\": \"Operator Utilities\",\n    \"itemGroup.spawnEggs\": \"Spawn Eggs\",\n    \"itemGroup.consumables\": \"Consumables\",\n    \"itemGroup.foodAndDrink\": \"Food & Drinks\",\n    \"itemGroup.crafting\": \"Crafting\",\n    \"itemGroup.ingredients\": \"Ingredients\",\n    \"subtitles.chiseled_bookshelf.insert\": \"Book placed\",\n    \"subtitles.chiseled_bookshelf.insert_enchanted\": \"Enchanted book placed\",\n    \"subtitles.chiseled_bookshelf.take\": \"Book taken\",\n    \"subtitles.chiseled_bookshelf.take_enchanted\": \"Enchanted book taken\",\n    \"subtitles.entity.camel.ambient\": \"Camel grunts\",\n    \"subtitles.entity.camel.dash\": \"Camel yeets\",\n    \"subtitles.entity.camel.dash_ready\": \"Camel recovers\",\n    \"subtitles.entity.camel.death\": \"Camel dies\",\n    \"subtitles.entity.camel.eat\": \"Camel eats\",\n    \"subtitles.entity.camel.hurt\": \"Camel hurts\",\n    \"subtitles.entity.camel.saddle\": \"Saddle equips\",\n    \"subtitles.entity.camel.sit\": \"Camel sits down\",\n    \"subtitles.entity.camel.stand\": \"Camel stands up\",\n    \"subtitles.entity.camel.step\": \"Camel steps\",\n    \"subtitles.entity.camel.step_sand\": \"Camel sands\",\n    \"subtitles.entity.enderman.scream\": \"Enderman screams\",\n    \"subtitles.entity.tadpole.grow_up\": \"Tadpole grows up\",\n    \"telemetry_info.screen.title\": \"Telemetry Data Collection\",\n    \"telemetry_info.screen.description\": \"Collecting this data helps us improve Minecraft by guiding us in directions that are relevant to our players.\\nYou can also send in additional feedback to help us keep improving Minecraft.\",\n    \"telemetry_info.button.show_data\": \"Open My Data\",\n    \"telemetry_info.button.give_feedback\": \"Give Feedback\",\n    \"telemetry_info.property_title\": \"Included Data\",\n    \"telemetry.property.user_id.title\": \"User ID\",\n    \"telemetry.property.client_id.title\": \"Client ID\",\n    \"telemetry.property.minecraft_session_id.title\": \"Minecraft Session ID\",\n    \"telemetry.property.game_version.title\": \"Game Version\",\n    \"telemetry.property.operating_system.title\": \"Operating System\",\n    \"telemetry.property.platform.title\": \"Platform\",\n    \"telemetry.property.client_modded.title\": \"Client Modded\",\n    \"telemetry.property.event_timestamp_utc.title\": \"Event Timestamp (UTC)\",\n    \"telemetry.property.opt_in.title\": \"Opt-In\",\n    \"telemetry.property.world_session_id.title\": \"World Session ID\",\n    \"telemetry.property.server_modded.title\": \"Server Modded\",\n    \"telemetry.property.server_type.title\": \"Server Type\",\n    \"telemetry.property.frame_rate_samples.title\": \"Frame Rate Samples (FPS)\",\n    \"telemetry.property.render_time_samples.title\": \"Render Time Samples\",\n    \"telemetry.property.used_memory_samples.title\": \"Used Random Access Memory\",\n    \"telemetry.property.number_of_samples.title\": \"Sample Count\",\n    \"telemetry.property.render_distance.title\": \"Render Distance\",\n    \"telemetry.property.dedicated_memory_kb.title\": \"Dedicated Memory (kB)\",\n    \"telemetry.property.game_mode.title\": \"Game Mode\",\n    \"telemetry.property.seconds_since_load.title\": \"Time Since Load (Seconds)\",\n    \"telemetry.property.ticks_since_load.title\": \"Time Since Load (Ticks)\",\n    \"telemetry.property.world_load_time_ms.title\": \"World Load Time (Milliseconds)\",\n    \"telemetry.property.new_world.title\": \"New World\",\n    \"telemetry.event.required\": \"%s (Required)\",\n    \"telemetry.event.optional\": \"%s (Optional)\",\n    \"telemetry.event.world_loaded.title\": \"World Loaded\",\n    \"telemetry.event.world_loaded.description\": \"Knowing how players play Minecraft (such as Game Mode, client or server modded, and game version) allows us to focus game updates to improve the areas that players care about most.\\nThe World Loaded event is paired with the World Unloaded event to calculate how long the play session has lasted.\",\n    \"telemetry.event.world_unloaded.title\": \"World Unloaded\",\n    \"telemetry.event.world_unloaded.description\": \"This event is paired with the World Loaded event to calculate how long the world session has lasted.\\nThe duration (in seconds and ticks) is measured when a world session has ended (quitting to title, disconnecting from a server).\",\n    \"telemetry.event.performance_metrics.title\": \"Performance Metrics\",\n    \"telemetry.event.performance_metrics.description\": \"Knowing the overall performance profile of Minecraft helps us tune and optimize the game for a wide range of machine specifications and operating systems. \\nGame version is included to help us compare the performance profile for new versions of Minecraft.\",\n    \"telemetry.event.world_load_times.title\": \"World Load Times\",\n    \"telemetry.event.world_load_times.description\": \"It�s important for us to understand how long it takes to join a world, and how that changes over time. For example, when we add new features or do larger technical changes, we need to see what impact that had on load times.\",\n    \"argument.resource.not_found\": \"Can't find element '%s' of type '%s'\",\n    \"argument.resource.invalid_type\": \"Element '%s' has wrong type '%s' (expected '%s')\",\n    \"argument.resource_tag.not_found\": \"Can't find tag '%s' of type '%s'\",\n    \"argument.resource_tag.invalid_type\": \"Tag '%s' has wrong type '%s' (expected '%s')\",\n    \"arguments.nbtpath.too_deep\": \"Resulting NBT too deeply nested\",\n    \"arguments.nbtpath.too_large\": \"Resulting NBT too large\",\n    \"argument.gamemode.invalid\": \"Unknown gamemode: %s\",\n    \"entity.not_summonable\": \"Can't summon entity of type %s\",\n    \"commands.datapack.enable.failed.no_flags\": \"Pack '%s' cannot be enabled, since required flags are not enabled in this world: %s!\",\n    \"commands.fillbiome.toobig\": \"Too many blocks in the specified volume (maximum %s, specified %s)\",\n    \"commands.fillbiome.success\": \"Biomes set between %s, %s, %s and %s, %s, %s\",\n    \"commands.fillbiome.success.count\": \"%s biome entries set between %s, %s, %s and %s, %s, %s\",\n    \"gamerule.blockExplosionDropDecay\": \"In block interaction explosions, some blocks won't drop their loot\",\n    \"gamerule.blockExplosionDropDecay.description\": \"Some of the drops from blocks destroyed by explosions caused by block interactions are lost in the explosion.\",\n    \"gamerule.mobExplosionDropDecay\": \"In mob explosions, some blocks won't drop their loot\",\n    \"gamerule.mobExplosionDropDecay.description\": \"Some of the drops from blocks destroyed by explosions caused by mobs are lost in the explosion.\",\n    \"gamerule.tntExplosionDropDecay\": \"In TNT explosions, some blocks won't drop their loot\",\n    \"gamerule.tntExplosionDropDecay.description\": \"Some of the drops from blocks destroyed by explosions caused by TNT are lost in the explosion.\",\n    \"gamerule.snowAccumulationHeight\": \"Snow accumulation height\",\n    \"gamerule.snowAccumulationHeight.description\": \"When it snows, layers of snow form on the ground up to at most this number of layers.\",\n    \"gamerule.waterSourceConversion\": \"Water converts to source\",\n    \"gamerule.waterSourceConversion.description\": \"When flowing water is surrounded on two sides by water sources it converts into a source.\",\n    \"gamerule.lavaSourceConversion\": \"Lava converts to source\",\n    \"gamerule.lavaSourceConversion.description\": \"When flowing lava is surrounded on two sides by lava sources it converts into a source.\",\n    \"gamerule.globalSoundEvents\": \"Global sound events\",\n    \"gamerule.globalSoundEvents.description\": \"When certain game events happen, like a boss spawning, the sound is heard everywhere.\",\n    \"pack.source.feature\": \"feature\",\n    \"mco.gui.ok\": \"Ok\",\n    \"mco.gui.button\": \"Button\",\n    \"mco.terms.buttons.agree\": \"Agree\",\n    \"mco.terms.buttons.disagree\": \"Don't agree\",\n    \"mco.terms.title\": \"Realms Terms of Service\",\n    \"mco.terms.sentence.1\": \"I agree to the Minecraft Realms\",\n    \"mco.terms.sentence.2\": \"Terms of Service\",\n    \"mco.selectServer.play\": \"Play\",\n    \"mco.selectServer.configure\": \"Configure\",\n    \"mco.selectServer.configureRealm\": \"Configure realm\",\n    \"mco.selectServer.leave\": \"Leave realm\",\n    \"mco.selectServer.create\": \"Create realm\",\n    \"mco.selectServer.purchase\": \"Add Realm\",\n    \"mco.selectServer.buy\": \"Buy a realm!\",\n    \"mco.selectServer.trial\": \"Get a trial!\",\n    \"mco.selectServer.close\": \"Close\",\n    \"mco.selectServer.expiredTrial\": \"Your trial has ended\",\n    \"mco.selectServer.expiredList\": \"Your subscription has expired\",\n    \"mco.selectServer.expiredSubscribe\": \"Subscribe\",\n    \"mco.selectServer.expiredRenew\": \"Renew\",\n    \"mco.selectServer.expired\": \"Expired realm\",\n    \"mco.selectServer.open\": \"Open realm\",\n    \"mco.selectServer.closed\": \"Closed realm\",\n    \"mco.selectServer.openserver\": \"Open realm\",\n    \"mco.selectServer.closeserver\": \"Close realm\",\n    \"mco.selectServer.minigame\": \"Minigame:\",\n    \"mco.selectServer.uninitialized\": \"Click to start your new realm!\",\n    \"mco.selectServer.expires.days\": \"Expires in %s days\",\n    \"mco.selectServer.expires.day\": \"Expires in a day\",\n    \"mco.selectServer.expires.soon\": \"Expires soon\",\n    \"mco.selectServer.note\": \"Note:\",\n    \"mco.selectServer.mapOnlySupportedForVersion\": \"This map is unsupported in %s\",\n    \"mco.selectServer.minigameNotSupportedInVersion\": \"Can't play this minigame in %s\",\n    \"mco.selectServer.popup\": \"Realms is a safe, simple way to enjoy an online Minecraft world with up to ten friends at a time.   It supports loads of minigames and plenty of custom worlds! Only the owner of the realm needs to pay.\",\n    \"mco.configure.world.settings.title\": \"Settings\",\n    \"mco.configure.world.title\": \"Configure realm:\",\n    \"mco.configure.worlds.title\": \"Worlds\",\n    \"mco.configure.world.name\": \"Realm name\",\n    \"mco.configure.world.description\": \"Realm description\",\n    \"mco.configure.world.location\": \"Location\",\n    \"mco.configure.world.invited\": \"Invited\",\n    \"mco.configure.world.invite.narration\": \"You have %s new invites\",\n    \"mco.configure.world.buttons.edit\": \"Settings\",\n    \"mco.configure.world.buttons.done\": \"Done\",\n    \"mco.configure.world.buttons.delete\": \"Delete\",\n    \"mco.configure.world.buttons.open\": \"Open realm\",\n    \"mco.configure.world.buttons.close\": \"Close realm\",\n    \"mco.configure.world.buttons.invite\": \"Invite player\",\n    \"mco.configure.world.buttons.activity\": \"Player activity\",\n    \"mco.configure.world.activityfeed.disabled\": \"Player feed temporarily disabled\",\n    \"mco.configure.world.buttons.moreoptions\": \"More options\",\n    \"mco.configure.world.invite.profile.name\": \"Name\",\n    \"mco.configure.world.uninvite.question\": \"Are you sure that you want to uninvite\",\n    \"mco.configure.world.status\": \"Status\",\n    \"mco.configure.world.invites.ops.tooltip\": \"Operator\",\n    \"mco.configure.world.invites.normal.tooltip\": \"Normal user\",\n    \"mco.configure.world.invites.remove.tooltip\": \"Remove\",\n    \"mco.configure.world.closing\": \"Closing the realm...\",\n    \"mco.configure.world.opening\": \"Opening the realm...\",\n    \"mco.configure.world.buttons.players\": \"Players\",\n    \"mco.configure.world.buttons.settings\": \"Settings\",\n    \"mco.configure.world.buttons.subscription\": \"Subscription\",\n    \"mco.configure.world.buttons.options\": \"World options\",\n    \"mco.configure.world.backup\": \"World backups\",\n    \"mco.configure.world.buttons.resetworld\": \"Reset world\",\n    \"mco.configure.world.buttons.switchminigame\": \"Switch minigame\",\n    \"mco.configure.current.minigame\": \"Current\",\n    \"mco.configure.world.subscription.title\": \"Your subscription\",\n    \"mco.configure.world.subscription.timeleft\": \"Time left\",\n    \"mco.configure.world.subscription.unknown\": \"Unknown\",\n    \"mco.configure.world.subscription.recurring.daysleft\": \"Renewed automatically in\",\n    \"mco.configure.world.subscription.less_than_a_day\": \"Less than a day\",\n    \"mco.configure.world.subscription.expired\": \"Expired\",\n    \"mco.configure.world.subscription.start\": \"Start date\",\n    \"mco.configure.world.subscription.extend\": \"Extend subscription\",\n    \"mco.configure.world.subscription.day\": \"day\",\n    \"mco.configure.world.subscription.month\": \"month\",\n    \"mco.configure.world.subscription.days\": \"days\",\n    \"mco.configure.world.subscription.months\": \"months\",\n    \"mco.configure.world.pvp\": \"PVP\",\n    \"mco.configure.world.spawn_toggle.title\": \"Warning!\",\n    \"mco.configure.world.spawn_toggle.message\": \"Turning this option off will REMOVE ALL existing entities of that type\",\n    \"mco.configure.world.spawn_toggle.message.npc\": \"Turning this option off will REMOVE ALL existing entities of that type, like Villagers\",\n    \"mco.configure.world.spawnAnimals\": \"Spawn animals\",\n    \"mco.configure.world.spawnNPCs\": \"Spawn NPCs\",\n    \"mco.configure.world.spawnMonsters\": \"Spawn monsters\",\n    \"mco.configure.world.spawnProtection\": \"Spawn protection\",\n    \"mco.configure.world.commandBlocks\": \"Command blocks\",\n    \"mco.configure.world.forceGameMode\": \"Force game mode\",\n    \"mco.configure.world.slot\": \"World %s\",\n    \"mco.configure.world.slot.empty\": \"Empty\",\n    \"mco.create.world.wait\": \"Creating the realm...\",\n    \"mco.create.world.error\": \"You must enter a name!\",\n    \"mco.create.world.subtitle\": \"Optionally, select what world to put on your new realm\",\n    \"mco.create.world.skip\": \"Skip\",\n    \"mco.reset.world.title\": \"Reset world\",\n    \"mco.reset.world.warning\": \"This will replace the current world of your realm\",\n    \"mco.reset.world.seed\": \"Seed (Optional)\",\n    \"mco.reset.world.resetting.screen.title\": \"Resetting world...\",\n    \"mco.reset.world.generate\": \"New world\",\n    \"mco.reset.world.upload\": \"Upload world\",\n    \"mco.reset.world.adventure\": \"Adventures\",\n    \"mco.reset.world.template\": \"World templates\",\n    \"mco.reset.world.experience\": \"Experiences\",\n    \"mco.reset.world.inspiration\": \"Inspiration\",\n    \"mco.minigame.world.title\": \"Switch realm to minigame\",\n    \"mco.minigame.world.info.line1\": \"This will temporarily replace your world with a minigame!\",\n    \"mco.minigame.world.info.line2\": \"You can later return to your original world without losing anything.\",\n    \"mco.minigame.world.selected\": \"Selected minigame:\",\n    \"mco.minigame.world.noSelection\": \"Please make a selection\",\n    \"mco.minigame.world.startButton\": \"Switch\",\n    \"mco.minigame.world.starting.screen.title\": \"Starting minigame...\",\n    \"mco.minigame.world.changeButton\": \"Select another minigame\",\n    \"mco.minigame.world.stopButton\": \"End minigame\",\n    \"mco.minigame.world.switch.title\": \"Switch minigame\",\n    \"mco.minigame.world.switch.new\": \"Select another minigame?\",\n    \"mco.minigame.world.restore.question.line1\": \"The minigame will end and your realm will be restored.\",\n    \"mco.minigame.world.restore.question.line2\": \"Are you sure you want to continue?\",\n    \"mco.minigame.world.restore\": \"Ending minigame...\",\n    \"mco.configure.world.slot.tooltip\": \"Switch to world\",\n    \"mco.configure.world.slot.tooltip.minigame\": \"Switch to minigame\",\n    \"mco.configure.world.slot.tooltip.active\": \"Join\",\n    \"mco.configure.world.close.question.line1\": \"Your realm will become unavailable.\",\n    \"mco.configure.world.close.question.line2\": \"Are you sure you want to continue?\",\n    \"mco.configure.world.leave.question.line1\": \"If you leave this realm you won't see it unless you are invited again\",\n    \"mco.configure.world.leave.question.line2\": \"Are you sure you want to continue?\",\n    \"mco.configure.world.resourcepack.question.line1\": \"You need a custom resource pack to play on this realm\",\n    \"mco.configure.world.resourcepack.question.line2\": \"Do you want to download it and play?\",\n    \"mco.configure.world.reset.question.line1\": \"Your world will be regenerated and your current world will be lost\",\n    \"mco.configure.world.reset.question.line2\": \"Are you sure you want to continue?\",\n    \"mco.configure.world.restore.question.line1\": \"Your world will be restored to date '%s' (%s)\",\n    \"mco.configure.world.restore.question.line2\": \"Are you sure you want to continue?\",\n    \"mco.configure.world.restore.download.question.line1\": \"The world will be downloaded and added to your single player worlds.\",\n    \"mco.configure.world.restore.download.question.line2\": \"Do you want to continue?\",\n    \"mco.configure.world.slot.switch.question.line1\": \"Your realm will be switched to another world\",\n    \"mco.configure.world.slot.switch.question.line2\": \"Are you sure you want to continue?\",\n    \"mco.configure.world.switch.slot\": \"Create world\",\n    \"mco.configure.world.switch.slot.subtitle\": \"This world is empty, choose how to create your world\",\n    \"mco.minigame.world.slot.screen.title\": \"Switching world...\",\n    \"mco.configure.world.edit.subscreen.adventuremap\": \"Some settings are disabled since your current world is an adventure\",\n    \"mco.configure.world.edit.subscreen.experience\": \"Some settings are disabled since your current world is an experience\",\n    \"mco.configure.world.edit.subscreen.inspiration\": \"Some settings are disabled since your current world is an inspiration\",\n    \"mco.configure.world.edit.slot.name\": \"World name\",\n    \"mco.configure.world.players.title\": \"Players\",\n    \"mco.configure.world.players.error\": \"A player with the provided name does not exist\",\n    \"mco.configure.world.delete.button\": \"Delete realm\",\n    \"mco.configure.world.delete.question.line1\": \"Your realm will be permanently deleted\",\n    \"mco.configure.world.delete.question.line2\": \"Are you sure you want to continue?\",\n    \"mco.connect.connecting\": \"Connecting to the realm...\",\n    \"mco.connect.authorizing\": \"Logging in...\",\n    \"mco.connect.failed\": \"Failed to connect to the realm\",\n    \"mco.connect.success\": \"Done\",\n    \"mco.create.world\": \"Create\",\n    \"mco.create.world.reset.title\": \"Creating world...\",\n    \"mco.client.incompatible.title\": \"Client incompatible!\",\n    \"mco.client.incompatible.msg.line1\": \"Your client is not compatible with Realms.\",\n    \"mco.client.incompatible.msg.line2\": \"Please use the most recent version of Minecraft.\",\n    \"mco.client.incompatible.msg.line3\": \"Realms is not compatible with snapshot versions.\",\n    \"mco.backup.button.restore\": \"Restore\",\n    \"mco.backup.generate.world\": \"Generate world\",\n    \"mco.backup.restoring\": \"Restoring your realm\",\n    \"mco.backup.button.download\": \"Download latest\",\n    \"mco.backup.changes.tooltip\": \"Changes\",\n    \"mco.backup.nobackups\": \"This realm doesn't have any backups currently.\",\n    \"mco.backup.button.upload\": \"Upload world\",\n    \"mco.backup.button.reset\": \"Reset world\",\n    \"mco.download.title\": \"Downloading latest world\",\n    \"mco.download.cancelled\": \"Download cancelled\",\n    \"mco.download.failed\": \"Download failed\",\n    \"mco.download.done\": \"Download done\",\n    \"mco.download.downloading\": \"Downloading\",\n    \"mco.download.extracting\": \"Extracting\",\n    \"mco.download.preparing\": \"Preparing download\",\n    \"mco.download.confirmation.line1\": \"The world you are going to download is larger than %s\",\n    \"mco.download.confirmation.line2\": \"You won't be able to upload this world to your realm again\",\n    \"mco.template.title\": \"World templates\",\n    \"mco.template.title.minigame\": \"Minigames\",\n    \"mco.template.button.select\": \"Select\",\n    \"mco.template.button.trailer\": \"Trailer\",\n    \"mco.template.button.publisher\": \"Publisher\",\n    \"mco.template.default.name\": \"World template\",\n    \"mco.template.name\": \"Template\",\n    \"mco.template.info.tooltip\": \"Publisher website\",\n    \"mco.template.trailer.tooltip\": \"Map trailer\",\n    \"mco.template.select.none\": \"Oops, it looks like this content category is currently empty.\\nPlease check back later for new content, or if you're a creator,\\n%s.\",\n    \"mco.template.select.none.linkTitle\": \"consider submitting something yourself\",\n    \"mco.template.select.failure\": \"We couldn't retrieve the list of content for this category.\\nPlease check your internet connection, or try again later.\",\n    \"mco.template.select.narrate.authors\": \"Authors: %s\",\n    \"mco.template.select.narrate.version\": \"version %s\",\n    \"mco.invites.button.accept\": \"Accept\",\n    \"mco.invites.button.reject\": \"Reject\",\n    \"mco.invites.title\": \"Pending Invites\",\n    \"mco.invites.pending\": \"New invites!\",\n    \"mco.invites.nopending\": \"No pending invites!\",\n    \"mco.upload.select.world.title\": \"Upload world\",\n    \"mco.upload.select.world.subtitle\": \"Please select a singleplayer world to upload\",\n    \"mco.upload.select.world.none\": \"No singleplayer worlds found!\",\n    \"mco.upload.button.name\": \"Upload\",\n    \"mco.upload.verifying\": \"Verifying your world\",\n    \"mco.upload.preparing\": \"Preparing your world\",\n    \"mco.upload.close.failure\": \"Could not close your realm, please try again later\",\n    \"mco.upload.size.failure.line1\": \"'%s' is too big!\",\n    \"mco.upload.size.failure.line2\": \"It is %s. The maximum allowed size is %s.\",\n    \"mco.upload.uploading\": \"Uploading '%s'\",\n    \"mco.upload.done\": \"Upload done\",\n    \"mco.upload.failed\": \"Upload failed! (%s)\",\n    \"mco.upload.cancelled\": \"Upload cancelled\",\n    \"mco.upload.hardcore\": \"Hardcore worlds can't be uploaded!\",\n    \"mco.activity.title\": \"Player activity\",\n    \"mco.activity.noactivity\": \"No activity for the past %s days\",\n    \"mco.errorMessage.6001\": \"Client outdated\",\n    \"mco.errorMessage.6002\": \"Terms of service not accepted\",\n    \"mco.errorMessage.6003\": \"Download limit reached\",\n    \"mco.errorMessage.6004\": \"Upload limit reached\",\n    \"mco.errorMessage.connectionFailure\": \"An error occurred, please try again later.\",\n    \"mco.errorMessage.serviceBusy\": \"Realms is busy at the moment.\\nPlease try connecting to your Realm again in a couple of minutes.\",\n    \"mco.trial.message.line1\": \"Want to get your own realm?\",\n    \"mco.trial.message.line2\": \"Click here for more info!\",\n    \"mco.brokenworld.play\": \"Play\",\n    \"mco.brokenworld.download\": \"Download\",\n    \"mco.brokenworld.downloaded\": \"Downloaded\",\n    \"mco.brokenworld.reset\": \"Reset\",\n    \"mco.brokenworld.title\": \"Your current world is no longer supported\",\n    \"mco.brokenworld.message.line1\": \"Please reset or select another world.\",\n    \"mco.brokenworld.message.line2\": \"You can also choose to download the world to singleplayer.\",\n    \"mco.brokenworld.minigame.title\": \"This minigame is no longer supported\",\n    \"mco.brokenworld.nonowner.title\": \"World is out of date\",\n    \"mco.brokenworld.nonowner.error\": \"Please wait for the realm owner to reset the world\",\n    \"mco.error.invalid.session.title\": \"Invalid session\",\n    \"mco.error.invalid.session.message\": \"Please try restarting Minecraft\",\n    \"mco.news\": \"Realms news\",\n    \"mco.account.privacyinfo\": \"Mojang implements certain procedures to help protect children and their privacy including complying with the Children�s Online Privacy Protection Act (COPPA) and General Data Protection Regulation (GDPR).\\n\\nYou may need to obtain parental consent before accessing your Realms account.\\n\\nIf you have an older Minecraft account (you log in with your username), you need to migrate the account to a Mojang account in order to access Realms.\",\n    \"mco.account.update\": \"Update account\",\n    \"mco.account.privacy.info\": \"Read more about Mojang and privacy laws\"\n  },\n  \"1.19.1\": {\n    \"gui.acknowledge\": \"Acknowledge\",\n    \"gui.socialInteractions.report\": \"Report\",\n    \"gui.socialInteractions.tooltip.report\": \"Report player\",\n    \"gui.socialInteractions.tooltip.report.disabled\": \"The reporting service is unavailable\",\n    \"gui.socialInteractions.tooltip.report.no_messages\": \"No reportable messages from player %s\",\n    \"gui.socialInteractions.tooltip.report.not_reportable\": \"This player can't be reported, because their chat messages can't be verified on this server\",\n    \"gui.socialInteractions.narration.hide\": \"Hide messages from %s\",\n    \"gui.socialInteractions.narration.show\": \"Show messages from %s\",\n    \"gui.socialInteractions.narration.report\": \"Report player %s\",\n    \"gui.chatReport.title\": \"Report Player\",\n    \"gui.chatReport.send\": \"Send Report\",\n    \"gui.chatReport.send.comments_too_long\": \"Please shorten the comment\",\n    \"gui.chatReport.send.no_reason\": \"Please select a report category\",\n    \"gui.chatReport.send.no_reported_messages\": \"Please select at least one chat message to report\",\n    \"gui.chatReport.send.too_many_messages\": \"Trying to include too many messages in the report\",\n    \"gui.chatReport.observed_what\": \"Why are you reporting this?\",\n    \"gui.chatReport.select_reason\": \"Select Report Category\",\n    \"gui.chatReport.more_comments\": \"Please describe what happened:\",\n    \"gui.chatReport.describe\": \"Sharing details will help us make a well-informed decision.\",\n    \"gui.chatReport.comments\": \"Comments edit box\",\n    \"gui.chatReport.read_info\": \"Learn About Reporting\",\n    \"gui.chatReport.select_chat\": \"Select Chat Messages to Report\",\n    \"gui.chatReport.selected_chat\": \"%s Chat Message(s) Selected to Report\",\n    \"gui.chatReport.report_sent_msg\": \"We�ve successfully received your report. Thank you!\\n\\nOur team will review it as soon as possible.\",\n    \"gui.chatReport.discard.title\": \"Discard report and comments?\",\n    \"gui.chatReport.discard.content\": \"If you leave, you'll lose this report and your comments.\\nAre you sure you want to leave?\",\n    \"gui.chatReport.discard.discard\": \"Leave and Discard Report\",\n    \"gui.chatReport.discard.return\": \"Continue Editing\",\n    \"gui.abuseReport.reason.title\": \"Select Report Category\",\n    \"gui.abuseReport.reason.description\": \"Description:\",\n    \"gui.abuseReport.reason.narration\": \"%s: %s\",\n    \"gui.abuseReport.reason.false_reporting\": \"False Reporting\",\n    \"gui.abuseReport.reason.child_sexual_exploitation_or_abuse\": \"Child sexual exploitation or abuse\",\n    \"gui.abuseReport.reason.child_sexual_exploitation_or_abuse.description\": \"Someone is talking about or otherwise promoting indecent behavior involving children.\",\n    \"gui.abuseReport.reason.terrorism_or_violent_extremism\": \"Terrorism or violent extremism\",\n    \"gui.abuseReport.reason.terrorism_or_violent_extremism.description\": \"Someone is talking about, promoting, or threatening to commit acts of terrorism or violent extremism for political, religious, ideological, or other reasons.\",\n    \"gui.abuseReport.reason.hate_speech\": \"Hate speech\",\n    \"gui.abuseReport.reason.hate_speech.description\": \"Someone is attacking you or another player based on characteristics of their identity, like religion, race, or sexuality.\",\n    \"gui.abuseReport.reason.harassment_or_bullying\": \"Harassment or bullying\",\n    \"gui.abuseReport.reason.harassment_or_bullying.description\": \"Someone is shaming, attacking, or bullying you or someone else. This includes when someone is repeatedly trying to contact you or someone else without consent or posting private personal information about you or someone else without consent (\\\"doxing\\\").\",\n    \"gui.abuseReport.reason.imminent_harm\": \"Imminent harm - Threat to harm others\",\n    \"gui.abuseReport.reason.imminent_harm.description\": \"Someone is threatening to harm you or someone else in real life.\",\n    \"gui.abuseReport.reason.defamation_impersonation_false_information\": \"Defamation, impersonation, or false information\",\n    \"gui.abuseReport.reason.defamation_impersonation_false_information.description\": \"Someone is damaging someone else's reputation, pretending to be someone they're not, or sharing false information with the aim to exploit or mislead others.\",\n    \"gui.abuseReport.reason.self_harm_or_suicide\": \"Imminent harm - Self-harm or suicide\",\n    \"gui.abuseReport.reason.self_harm_or_suicide.description\": \"Someone is threatening to harm themselves in real life or talking about harming themselves in real life.\",\n    \"gui.abuseReport.reason.alcohol_tobacco_drugs\": \"Drugs or alcohol\",\n    \"gui.abuseReport.reason.alcohol_tobacco_drugs.description\": \"Someone is encouraging others to partake in illegal drug related activities or encouraging underage drinking.\",\n    \"gui.abuseReport.reason.non_consensual_intimate_imagery\": \"Non-consensual intimate imagery\",\n    \"gui.abuseReport.reason.non_consensual_intimate_imagery.description\": \"Someone is talking about, sharing, or otherwise promoting private and intimate images.\",\n    \"gui.abuseReport.sending.title\": \"Sending your report...\",\n    \"gui.abuseReport.sent.title\": \"Report sent\",\n    \"gui.abuseReport.error.title\": \"Problem sending your report\",\n    \"gui.abuseReport.send.generic_error\": \"Encountered an unexpected error while sending your report.\",\n    \"gui.abuseReport.send.error_message\": \"An error was returned while sending your report:\\n'%s'\",\n    \"gui.abuseReport.send.service_unavailable\": \"Unable to reach the Abuse Reporting service. Please make sure you are connected to the internet and try again.\",\n    \"gui.abuseReport.send.http_error\": \"An unexpected HTTP error occurred while sending your report.\",\n    \"gui.abuseReport.send.json_error\": \"Encountered malformed payload while sending your report.\",\n    \"gui.chatSelection.title\": \"Select Chat Messages to Report\",\n    \"gui.chatSelection.context\": \"Messages surrounding this selection will be included to provide additional context\",\n    \"gui.chatSelection.selected\": \"%s/%s message(s) selected\",\n    \"gui.chatSelection.heading\": \"%s %s\",\n    \"gui.chatSelection.message.narrate\": \"%s said: %s at %s\",\n    \"gui.chatSelection.fold\": \"%s unrelated messages hidden\",\n    \"gui.multiLineEditBox.character_limit\": \"%s/%s\",\n    \"gui.banned.title.temporary\": \"Account temporarily suspended\",\n    \"gui.banned.title.permanent\": \"Account permanently banned\",\n    \"gui.banned.description\": \"%s\\n\\n%s\\n\\nLearn more at the following link: %s\",\n    \"gui.banned.description.reason\": \"We recently received a report for bad behavior by your account. Our moderators have now reviewed your case and identified it as %s, which goes against the Minecraft Community Standards.\",\n    \"gui.banned.description.reason_id\": \"Code: %s\",\n    \"gui.banned.description.reason_id_message\": \"Code: %s - %s\",\n    \"gui.banned.description.unknownreason\": \"We recently received a report for bad behavior by your account. Our moderators have now reviewed your case and identified that it goes against the Minecraft Community Standards.\",\n    \"gui.banned.description.temporary.duration\": \"Your account is temporarily suspended and will be reactivated in %s.\",\n    \"gui.banned.description.temporary\": \"%s Until then, you can�t play online or join Realms.\",\n    \"gui.banned.description.permanent\": \"Your account is permanently banned, which means you can�t play online or join Realms.\",\n    \"menu.playerReporting\": \"Player Reporting\",\n    \"multiplayer.disconnect.unsigned_chat\": \"Received chat packet with missing or invalid signature.\",\n    \"multiplayer.disconnect.too_many_pending_chats\": \"Too many unacknowledged chat messages\",\n    \"multiplayer.disconnect.chat_validation_failed\": \"Chat message validation failure\",\n    \"multiplayer.unsecureserver.toast.title\": \"Chat messages can't be verified\",\n    \"multiplayer.unsecureserver.toast\": \"Messages sent on this server may be modified and might not reflect the original message\",\n    \"chat.previewInput\": \"Press [%s] to preview\",\n    \"chat.tag.not_secure\": \"This message is not secure, which means that it might have been modified by the server\",\n    \"chat.tag.modified\": \"This message has been modified by the server.\",\n    \"chat.tag.modified.original\": \"Original text: %s\",\n    \"chat.tag.filtered\": \"This message has been filtered by the server.\",\n    \"chat.filtered_full\": \"The server has hidden your message for some players.\",\n    \"disconnect.loginFailedInfo.userBanned\": \"You are banned from playing online\",\n    \"options.chatPreview.live\": \"While Typing\",\n    \"options.chatPreview.confirm\": \"When Sending\",\n    \"options.chatPreview.tooltip.off\": \"Any modifications applied to your chat messages by a server will not be previewed and will be treated as insecure.\",\n    \"options.chatPreview.tooltip.live\": \"If a server uses Chat Previews: Any modifications applied to your chat messages by a server will be dynamically sent for previewing as the chat message is typed.\",\n    \"options.chatPreview.tooltip.confirm\": \"If a server uses Chat Previews: A chat preview is only generated when attempting to send a message that does not have a preview or is waiting for a preview.\\nSending the message requires confirmation.\",\n    \"title.multiplayer.disabled.banned.temporary\": \"Your account is temporarily suspended from online play\",\n    \"title.multiplayer.disabled.banned.permanent\": \"Your account is permanently suspended from online play\",\n    \"gui.minutes\": \"%s minute(s)\",\n    \"gui.hours\": \"%s hour(s)\",\n    \"gui.days\": \"%s day(s)\"\n  },\n  \"1.19\": {\n    \"selectWorld.loading_list\": \"Loading world list\",\n    \"flat_world_preset.unknown\": \"???\",\n    \"flat_world_preset.minecraft.classic_flat\": \"Classic Flat\",\n    \"flat_world_preset.minecraft.tunnelers_dream\": \"Tunnelers' Dream\",\n    \"flat_world_preset.minecraft.water_world\": \"Water World\",\n    \"flat_world_preset.minecraft.overworld\": \"Overworld\",\n    \"flat_world_preset.minecraft.snowy_kingdom\": \"Snowy Kingdom\",\n    \"flat_world_preset.minecraft.bottomless_pit\": \"Bottomless Pit\",\n    \"flat_world_preset.minecraft.desert\": \"Desert\",\n    \"flat_world_preset.minecraft.redstone_ready\": \"Redstone Ready\",\n    \"flat_world_preset.minecraft.the_void\": \"The Void\",\n    \"generator.minecraft.normal\": \"Default\",\n    \"generator.minecraft.flat\": \"Superflat\",\n    \"generator.minecraft.large_biomes\": \"Large Biomes\",\n    \"generator.minecraft.amplified\": \"AMPLIFIED\",\n    \"generator.minecraft.amplified.info\": \"Notice: Just for fun! Requires a beefy computer.\",\n    \"generator.minecraft.debug_all_block_states\": \"Debug Mode\",\n    \"generator.minecraft.single_biome_surface\": \"Single Biome\",\n    \"multiplayer.disconnect.missing_public_key\": \"Missing profile public key.\\nThis server requires secure profiles.\",\n    \"multiplayer.disconnect.invalid_public_key_signature\": \"Invalid signature for profile public key.\\nTry restarting your game.\",\n    \"multiplayer.disconnect.invalid_public_key\": \"Unable to parse profile public key.\",\n    \"multiplayer.disconnect.out_of_order_chat\": \"Out-of-order chat packet received. Did your system time change?\",\n    \"chatPreview.warning.title\": \"This server uses Chat Preview\",\n    \"chatPreview.warning.content\": \"Chat Preview allows the server to see your messages in real time as you type them, even before they�re sent. This is often used to preview your message with styling applied.\\n\\nChat Preview is on by default, but can be turned off in Chat Settings.\",\n    \"chatPreview.warning.check\": \"Do not notify again for this server\",\n    \"chatPreview.warning.toast.title\": \"Chat Preview is enabled\",\n    \"chatPreview.warning.toast\": \"This server uses Chat Preview and can see your messages as you type them, even before they're sent. You can turn this off in Chat Settings.\",\n    \"chat.disabled.profile.moreInfo\": \"Chat not allowed by account settings. Cannot send or view messages.\",\n    \"chat.preview\": \"Type to preview\",\n    \"options.darknessEffectScale\": \"Darkness Pulsing\",\n    \"options.darknessEffectScale.tooltip\": \"Controls how much the Darkness effect pulses when a Warden or Sculk Shrieker gives it to you.\",\n    \"options.chatPreview\": \"Chat Preview\",\n    \"options.chatPreview.tooltip\": \"Chat Preview allows servers to see your messages as you type, which allows them to style your message. You can still chat if you turn this off.\",\n    \"options.onlyShowSecureChat\": \"Only Show Secure Chat\",\n    \"options.onlyShowSecureChat.tooltip\": \"Only display messages from other players that can be verified to have been sent by that player, and have not been modified.\",\n    \"options.directionalAudio\": \"Directional Audio\",\n    \"options.directionalAudio.on.tooltip\": \"Uses HRTF-based directional audio to improve the simulation of 3D sound. Requires HRTF compatible audio hardware, and is best experienced with headphones.\",\n    \"options.directionalAudio.off.tooltip\": \"Classic Stereo sound\",\n    \"block.minecraft.mangrove_planks\": \"Mangrove Planks\",\n    \"block.minecraft.mangrove_propagule\": \"Mangrove Propagule\",\n    \"block.minecraft.mangrove_door\": \"Mangrove Door\",\n    \"block.minecraft.mangrove_wood\": \"Mangrove Wood\",\n    \"block.minecraft.mangrove_log\": \"Mangrove Log\",\n    \"block.minecraft.mangrove_roots\": \"Mangrove Roots\",\n    \"block.minecraft.muddy_mangrove_roots\": \"Muddy Mangrove Roots\",\n    \"block.minecraft.stripped_mangrove_log\": \"Stripped Mangrove Log\",\n    \"block.minecraft.stripped_mangrove_wood\": \"Stripped Mangrove Wood\",\n    \"block.minecraft.mangrove_leaves\": \"Mangrove Leaves\",\n    \"block.minecraft.mud_brick_slab\": \"Mud Brick Slab\",\n    \"block.minecraft.mangrove_slab\": \"Mangrove Slab\",\n    \"block.minecraft.mangrove_stairs\": \"Mangrove Stairs\",\n    \"block.minecraft.mangrove_sign\": \"Mangrove Sign\",\n    \"block.minecraft.mangrove_wall_sign\": \"Mangrove Wall Sign\",\n    \"block.minecraft.mangrove_pressure_plate\": \"Mangrove Pressure Plate\",\n    \"block.minecraft.mangrove_button\": \"Mangrove Button\",\n    \"block.minecraft.mangrove_fence\": \"Mangrove Fence\",\n    \"block.minecraft.mangrove_fence_gate\": \"Mangrove Fence Gate\",\n    \"block.minecraft.mangrove_trapdoor\": \"Mangrove Trapdoor\",\n    \"block.minecraft.packed_mud\": \"Packed Mud\",\n    \"block.minecraft.mud_bricks\": \"Mud Bricks\",\n    \"block.minecraft.mud_brick_stairs\": \"Mud Brick Stairs\",\n    \"block.minecraft.potted_mangrove_propagule\": \"Potted Mangrove Propagule\",\n    \"block.minecraft.mud_brick_wall\": \"Mud Brick Wall\",\n    \"block.minecraft.mud\": \"Mud\",\n    \"block.minecraft.sculk\": \"Sculk\",\n    \"block.minecraft.sculk_catalyst\": \"Sculk Catalyst\",\n    \"block.minecraft.sculk_shrieker\": \"Sculk Shrieker\",\n    \"block.minecraft.sculk_vein\": \"Sculk Vein\",\n    \"block.minecraft.ochre_froglight\": \"Ochre Froglight\",\n    \"block.minecraft.verdant_froglight\": \"Verdant Froglight\",\n    \"block.minecraft.pearlescent_froglight\": \"Pearlescent Froglight\",\n    \"block.minecraft.frogspawn\": \"Frogspawn\",\n    \"block.minecraft.reinforced_deepslate\": \"Reinforced Deepslate\",\n    \"item.minecraft.tadpole_bucket\": \"Bucket of Tadpole\",\n    \"item.minecraft.oak_chest_boat\": \"Oak Boat with Chest\",\n    \"item.minecraft.spruce_chest_boat\": \"Spruce Boat with Chest\",\n    \"item.minecraft.birch_chest_boat\": \"Birch Boat with Chest\",\n    \"item.minecraft.jungle_chest_boat\": \"Jungle Boat with Chest\",\n    \"item.minecraft.acacia_chest_boat\": \"Acacia Boat with Chest\",\n    \"item.minecraft.dark_oak_chest_boat\": \"Dark Oak Boat with Chest\",\n    \"item.minecraft.mangrove_boat\": \"Mangrove Boat\",\n    \"item.minecraft.mangrove_chest_boat\": \"Mangrove Boat with Chest\",\n    \"item.minecraft.recovery_compass\": \"Recovery Compass\",\n    \"item.minecraft.music_disc_5\": \"Music Disc\",\n    \"item.minecraft.music_disc_5.desc\": \"Samuel �berg - 5\",\n    \"item.minecraft.disc_fragment_5\": \"Disc Fragment\",\n    \"item.minecraft.disc_fragment_5.desc\": \"Music Disc - 5\",\n    \"item.minecraft.allay_spawn_egg\": \"Allay Spawn Egg\",\n    \"item.minecraft.frog_spawn_egg\": \"Frog Spawn Egg\",\n    \"item.minecraft.tadpole_spawn_egg\": \"Tadpole Spawn Egg\",\n    \"item.minecraft.warden_spawn_egg\": \"Warden Spawn Egg\",\n    \"item.minecraft.echo_shard\": \"Echo Shard\",\n    \"item.minecraft.goat_horn\": \"Goat Horn\",\n    \"instrument.minecraft.ponder_goat_horn\": \"Ponder\",\n    \"instrument.minecraft.sing_goat_horn\": \"Sing\",\n    \"instrument.minecraft.seek_goat_horn\": \"Seek\",\n    \"instrument.minecraft.feel_goat_horn\": \"Feel\",\n    \"instrument.minecraft.admire_goat_horn\": \"Admire\",\n    \"instrument.minecraft.call_goat_horn\": \"Call\",\n    \"instrument.minecraft.yearn_goat_horn\": \"Yearn\",\n    \"instrument.minecraft.dream_goat_horn\": \"Dream\",\n    \"entity.minecraft.allay\": \"Allay\",\n    \"entity.minecraft.chest_boat\": \"Boat with Chest\",\n    \"entity.minecraft.frog\": \"Frog\",\n    \"entity.minecraft.tadpole\": \"Tadpole\",\n    \"entity.minecraft.warden\": \"Warden\",\n    \"death.attack.onFire.item\": \"%1$s was burnt to a crisp whilst fighting %2$s wielding %3$s\",\n    \"death.attack.witherSkull.item\": \"%1$s was shot by a skull from %2$s using %3$s\",\n    \"death.attack.sonic_boom\": \"%1$s was obliterated by a sonically-charged shriek\",\n    \"death.attack.sonic_boom.item\": \"%1$s was obliterated by a sonically-charged shriek whilst trying to escape %2$s wielding %3$s\",\n    \"death.attack.sonic_boom.player\": \"%1$s was obliterated by a sonically-charged shriek whilst trying to escape %2$s\",\n    \"death.attack.sting.item\": \"%1$s was stung to death by %2$s using %3$s\",\n    \"effect.minecraft.darkness\": \"Darkness\",\n    \"enchantment.minecraft.swift_sneak\": \"Swift Sneak\",\n    \"subtitles.block.frogspawn.hatch\": \"Tadpole hatches\",\n    \"subtitles.block.sculk.charge\": \"Sculk bubbles\",\n    \"subtitles.block.sculk.spread\": \"Sculk spreads\",\n    \"subtitles.block.sculk_catalyst.bloom\": \"Sculk Catalyst blooms\",\n    \"subtitles.block.sculk_shrieker.shriek\": \"Sculk Shrieker shrieks\",\n    \"subtitles.entity.allay.death\": \"Allay dies\",\n    \"subtitles.entity.allay.hurt\": \"Allay hurts\",\n    \"subtitles.entity.allay.ambient_with_item\": \"Allay seeks\",\n    \"subtitles.entity.allay.ambient_without_item\": \"Allay yearns\",\n    \"subtitles.entity.allay.item_given\": \"Allay chortles\",\n    \"subtitles.entity.allay.item_taken\": \"Allay allays\",\n    \"subtitles.entity.allay.item_thrown\": \"Allay tosses\",\n    \"subtitles.entity.frog.ambient\": \"Frog croaks\",\n    \"subtitles.entity.frog.death\": \"Frog dies\",\n    \"subtitles.entity.frog.eat\": \"Frog eats\",\n    \"subtitles.entity.frog.hurt\": \"Frog hurts\",\n    \"subtitles.entity.frog.lay_spawn\": \"Frog lays spawn\",\n    \"subtitles.entity.frog.long_jump\": \"Frog jumps\",\n    \"subtitles.entity.goat.horn_break\": \"Goat Horn breaks off\",\n    \"subtitles.entity.parrot.imitate.warden\": \"Parrot whines\",\n    \"subtitles.entity.tadpole.death\": \"Tadpole dies\",\n    \"subtitles.entity.tadpole.flop\": \"Tadpole flops\",\n    \"subtitles.entity.tadpole.hurt\": \"Tadpole hurts\",\n    \"subtitles.entity.warden.roar\": \"Warden roars\",\n    \"subtitles.entity.warden.sniff\": \"Warden sniffs\",\n    \"subtitles.entity.warden.emerge\": \"Warden emerges\",\n    \"subtitles.entity.warden.dig\": \"Warden digs\",\n    \"subtitles.entity.warden.hurt\": \"Warden hurts\",\n    \"subtitles.entity.warden.death\": \"Warden dies\",\n    \"subtitles.entity.warden.step\": \"Warden steps\",\n    \"subtitles.entity.warden.listening\": \"Warden takes notice\",\n    \"subtitles.entity.warden.listening_angry\": \"Warden takes notice angrily\",\n    \"subtitles.entity.warden.heartbeat\": \"Warden's heart beats\",\n    \"subtitles.entity.warden.attack_impact\": \"Warden lands hit\",\n    \"subtitles.entity.warden.tendril_clicks\": \"Warden's tendrils click\",\n    \"subtitles.entity.warden.angry\": \"Warden rages\",\n    \"subtitles.entity.warden.agitated\": \"Warden groans angrily\",\n    \"subtitles.entity.warden.ambient\": \"Warden whines\",\n    \"subtitles.entity.warden.nearby_close\": \"Warden approaches\",\n    \"subtitles.entity.warden.nearby_closer\": \"Warden advances\",\n    \"subtitles.entity.warden.nearby_closest\": \"Warden draws close\",\n    \"subtitles.entity.warden.sonic_charge\": \"Warden charges\",\n    \"subtitles.entity.warden.sonic_boom\": \"Warden booms\",\n    \"subtitles.item.bucket.fill_tadpole\": \"Tadpole captured\",\n    \"subtitles.item.goat_horn.play\": \"Goat Horn plays\",\n    \"advancements.adventure.avoid_vibration.title\": \"Sneak 100\",\n    \"advancements.adventure.avoid_vibration.description\": \"Sneak near a Sculk Sensor or Warden to prevent it from detecting you\",\n    \"advancements.adventure.kill_mob_near_sculk_catalyst.title\": \"It Spreads\",\n    \"advancements.adventure.kill_mob_near_sculk_catalyst.description\": \"Kill a mob near a Sculk Catalyst\",\n    \"advancements.husbandry.froglights.title\": \"With Our Powers Combined!\",\n    \"advancements.husbandry.froglights.description\": \"Have all Froglights in your inventory\",\n    \"advancements.husbandry.tadpole_in_a_bucket.title\": \"Bukkit Bukkit\",\n    \"advancements.husbandry.tadpole_in_a_bucket.description\": \"Catch a Tadpole in a Bucket\",\n    \"advancements.husbandry.leash_all_frog_variants.title\": \"When the Squad Hops into Town\",\n    \"advancements.husbandry.leash_all_frog_variants.description\": \"Get each Frog variant on a Lead\",\n    \"advancements.husbandry.allay_deliver_item_to_player.title\": \"You've Got a Friend in Me\",\n    \"advancements.husbandry.allay_deliver_item_to_player.description\": \"Have an Allay deliver items to you\",\n    \"advancements.husbandry.allay_deliver_cake_to_note_block.title\": \"Birthday Song\",\n    \"advancements.husbandry.allay_deliver_cake_to_note_block.description\": \"Have an Allay drop a Cake at a Note Block\",\n    \"argument.enum.invalid\": \"Invalid value \\\"%s\\\"\",\n    \"commands.locate.structure.success\": \"The nearest %s is at %s (%s blocks away)\",\n    \"commands.locate.structure.not_found\": \"Could not find a structure of type \\\"%s\\\" nearby\",\n    \"commands.locate.structure.invalid\": \"There is no structure with type \\\"%s\\\"\",\n    \"commands.locate.biome.success\": \"The nearest %s is at %s (%s blocks away)\",\n    \"commands.locate.biome.not_found\": \"Could not find a biome of type \\\"%s\\\" within reasonable distance\",\n    \"commands.locate.biome.invalid\": \"There is no biome with type \\\"%s\\\"\",\n    \"commands.locate.poi.success\": \"The nearest %s is at %s (%s blocks away)\",\n    \"commands.locate.poi.not_found\": \"Could not find a point of interest of type \\\"%s\\\" within reasonable distance\",\n    \"commands.locate.poi.invalid\": \"There is no point of interest with type \\\"%s\\\"\",\n    \"commands.place.feature.failed\": \"Failed to place feature\",\n    \"commands.place.feature.invalid\": \"There is no feature with type \\\"%s\\\"\",\n    \"commands.place.feature.success\": \"Placed \\\"%s\\\" at %s, %s, %s\",\n    \"commands.place.jigsaw.failed\": \"Failed to generate jigsaw\",\n    \"commands.place.jigsaw.invalid\": \"There is no template pool with type \\\"%s\\\"\",\n    \"commands.place.jigsaw.success\": \"Generated jigsaw at %s, %s, %s\",\n    \"commands.place.structure.failed\": \"Failed to place structure\",\n    \"commands.place.structure.invalid\": \"There is no structure with type \\\"%s\\\"\",\n    \"commands.place.structure.success\": \"Generated structure \\\"%s\\\" at %s, %s, %s\",\n    \"commands.place.template.failed\": \"Failed to place template\",\n    \"commands.place.template.invalid\": \"There is no template with id \\\"%s\\\"\",\n    \"commands.place.template.success\": \"Loaded template \\\"%s\\\" at %s, %s, %s\",\n    \"biome.minecraft.deep_dark\": \"Deep Dark\",\n    \"biome.minecraft.mangrove_swamp\": \"Mangrove Swamp\",\n    \"gamerule.doWardenSpawning\": \"Spawn Wardens\",\n    \"outOfMemory.title\": \"Out of memory!\",\n    \"outOfMemory.message\": \"Minecraft has run out of memory.\\n\\nThis could be caused by a bug in the game or by the Java Virtual Machine not being allocated enough memory.\\n\\nTo prevent level corruption, the current game has quit. We've tried to free up enough memory to let you go back to the main menu and back to playing, but this may not have worked.\\n\\nPlease restart the game if you see this message again.\"\n  },\n  \"1.18\": {\n    \"selectWorld.conversion.tooltip\": \"This world must be opened in an older version (like 1.6.4) to be safely converted\",\n    \"selectWorld.incompatible_series\": \"Created by an incompatible version\",\n    \"options.gamma.default\": \"Default\",\n    \"options.simulationDistance\": \"Simulation Distance\",\n    \"options.prioritizeChunkUpdates\": \"Chunk Builder\",\n    \"options.prioritizeChunkUpdates.none\": \"Threaded\",\n    \"options.prioritizeChunkUpdates.byPlayer\": \"Semi Blocking\",\n    \"options.prioritizeChunkUpdates.nearby\": \"Fully Blocking\",\n    \"options.prioritizeChunkUpdates.none.tooltip\": \"Nearby chunks are compiled in parallel threads. This may result in brief visual holes when blocks are destroyed.\",\n    \"options.prioritizeChunkUpdates.byPlayer.tooltip\": \"Some actions within a chunk will recompile the chunk immediately. This includes block placing & destroying.\",\n    \"options.prioritizeChunkUpdates.nearby.tooltip\": \"Nearby chunks are always compiled immediately. This may impact game performance when blocks are placed or destroyed.\",\n    \"options.difficulty.online\": \"Server Difficulty\",\n    \"options.audioDevice\": \"Device\",\n    \"options.audioDevice.default\": \"System Default\",\n    \"options.online\": \"Online...\",\n    \"options.online.title\": \"Online Options\",\n    \"options.allowServerListing\": \"Allow Server Listings\",\n    \"options.allowServerListing.tooltip\": \"Servers may list online players as part of their public status.\\nWith this option off your name will not show up in such lists.\",\n    \"options.autosaveIndicator\": \"Autosave Indicator\",\n    \"options.hideLightningFlashes\": \"Hide Lightning Flashes\",\n    \"options.hideLightningFlashes.tooltip\": \"Prevents lightning bolts from making the sky flash. The bolts themselves will still be visible.\",\n    \"controls.keybinds\": \"Key Binds...\",\n    \"controls.keybinds.title\": \"Key Binds\",\n    \"item.minecraft.music_disc_otherside\": \"Music Disc\",\n    \"item.minecraft.music_disc_otherside.desc\": \"Lena Raine - otherside\",\n    \"subtitles.block.growing_plant.crop\": \"Plant cropped\",\n    \"subtitles.item.bundle.drop_contents\": \"Bundle empties\",\n    \"subtitles.item.bundle.insert\": \"Item packed\",\n    \"subtitles.item.bundle.remove_one\": \"Item unpacked\",\n    \"advancements.adventure.fall_from_world_height.title\": \"Caves & Cliffs\",\n    \"advancements.adventure.fall_from_world_height.description\": \"Free fall from the top of the world (build limit) to the bottom of the world and survive\",\n    \"advancements.adventure.play_jukebox_in_meadows.title\": \"Sound of Music\",\n    \"advancements.adventure.play_jukebox_in_meadows.description\": \"Make the Meadows come alive with the sound of music from a jukebox\",\n    \"advancements.adventure.trade_at_world_height.title\": \"Star Trader\",\n    \"advancements.adventure.trade_at_world_height.description\": \"Trade with a villager at the build height limit\",\n    \"advancements.nether.ride_strider_in_overworld_lava.title\": \"Feels like home\",\n    \"advancements.nether.ride_strider_in_overworld_lava.description\": \"Take a Strider for a loooong ride on a lava lake in the Overworld\",\n    \"commands.jfr.started\": \"JFR profiling started\",\n    \"commands.jfr.start.failed\": \"Failed to start JFR profiling\",\n    \"commands.jfr.stopped\": \"JFR profiling stopped and dumped to %s\",\n    \"commands.jfr.dump.failed\": \"Failed to dump JFR recording: %s\",\n    \"commands.worldborder.set.failed.far\": \"World border cannot be further out than %s blocks\",\n    \"biome.minecraft.old_growth_birch_forest\": \"Old Growth Birch Forest\",\n    \"biome.minecraft.old_growth_pine_taiga\": \"Old Growth Pine Taiga\",\n    \"biome.minecraft.old_growth_spruce_taiga\": \"Old Growth Spruce Taiga\",\n    \"biome.minecraft.frozen_peaks\": \"Frozen Peaks\",\n    \"biome.minecraft.grove\": \"Grove\",\n    \"biome.minecraft.jagged_peaks\": \"Jagged Peaks\",\n    \"biome.minecraft.meadow\": \"Meadow\",\n    \"biome.minecraft.snowy_plains\": \"Snowy Plains\",\n    \"biome.minecraft.snowy_slopes\": \"Snowy Slopes\",\n    \"biome.minecraft.sparse_jungle\": \"Sparse Jungle\",\n    \"biome.minecraft.stony_peaks\": \"Stony Peaks\",\n    \"biome.minecraft.stony_shore\": \"Stony Shore\",\n    \"biome.minecraft.windswept_forest\": \"Windswept Forest\",\n    \"biome.minecraft.windswept_gravelly_hills\": \"Windswept Gravelly Hills\",\n    \"biome.minecraft.windswept_hills\": \"Windswept Hills\",\n    \"biome.minecraft.windswept_savanna\": \"Windswept Savanna\",\n    \"biome.minecraft.wooded_badlands\": \"Wooded Badlands\"\n  },\n  \"1.17\": {\n    \"gui.socialInteractions.title\": \"Social Interactions\",\n    \"gui.socialInteractions.tab_all\": \"All\",\n    \"gui.socialInteractions.tab_hidden\": \"Hidden\",\n    \"gui.socialInteractions.tab_blocked\": \"Blocked\",\n    \"gui.socialInteractions.blocking_hint\": \"Manage with Microsoft account\",\n    \"gui.socialInteractions.status_hidden\": \"Hidden\",\n    \"gui.socialInteractions.status_blocked\": \"Blocked\",\n    \"gui.socialInteractions.status_offline\": \"Offline\",\n    \"gui.socialInteractions.status_hidden_offline\": \"Hidden - Offline\",\n    \"gui.socialInteractions.status_blocked_offline\": \"Blocked - Offline\",\n    \"gui.socialInteractions.server_label.single\": \"%s - %s player\",\n    \"gui.socialInteractions.server_label.multiple\": \"%s - %s players\",\n    \"gui.socialInteractions.search_hint\": \"Search...\",\n    \"gui.socialInteractions.search_empty\": \"Couldn't find any players with that name\",\n    \"gui.socialInteractions.empty_hidden\": \"No players hidden in chat\",\n    \"gui.socialInteractions.empty_blocked\": \"No blocked players in chat\",\n    \"gui.socialInteractions.hide\": \"Hide in Chat\",\n    \"gui.socialInteractions.show\": \"Show in Chat\",\n    \"gui.socialInteractions.hidden_in_chat\": \"Chat messages from %s will be hidden\",\n    \"gui.socialInteractions.shown_in_chat\": \"Chat messages from %s will be shown\",\n    \"gui.socialInteractions.tooltip.hide\": \"Hide messages from %s in chat\",\n    \"gui.socialInteractions.tooltip.show\": \"Show messages from %s in chat\",\n    \"selectWorld.pre_worldheight\": \"Loading of worlds with extended height is disabled.\",\n    \"selectWorld.backupQuestion.snapshot\": \"Do you really want to load this world?\",\n    \"selectWorld.backupWarning.snapshot\": \"This world was last played in version %s; you are on version %s. Please make a backup in case you experience world corruptions!\",\n    \"selectWorld.backupQuestion.downgrade\": \"Downgrading a world is not supported\",\n    \"selectWorld.backupWarning.downgrade\": \"This world was last played in version %s; you are on version %s. Downgrading a world could cause corruption - we cannot guarantee that it will load or work. If you still want to continue, please make a backup!\",\n    \"multiplayer.requiredTexturePrompt.line1\": \"This server requires the use of a custom resource pack.\",\n    \"multiplayer.requiredTexturePrompt.line2\": \"Rejecting this custom resource pack will disconnect you from this server.\",\n    \"multiplayer.requiredTexturePrompt.disconnect\": \"Server requires a custom resource pack\",\n    \"multiplayer.texturePrompt.failure.line1\": \"Server resource pack couldn't be applied\",\n    \"multiplayer.texturePrompt.failure.line2\": \"Any functionality that requires custom resources might not work as expected\",\n    \"multiplayer.texturePrompt.serverPrompt\": \"%s\\n\\nMessage from server:\\n%s\",\n    \"multiplayer.applyingPack\": \"Applying resource pack\",\n    \"multiplayer.status.incompatible\": \"Incompatible version!\",\n    \"multiplayer.disconnect.invalid_packet\": \"Server sent an invalid packet\",\n    \"multiplayer.disconnect.invalid_player_data\": \"Invalid player data\",\n    \"multiplayer.disconnect.incompatible\": \"Incompatible client! Please use %s\",\n    \"multiplayer.socialInteractions.not_available\": \"Social Interactions are only available in Multiplayer worlds\",\n    \"chat.disabled.options\": \"Chat disabled in client options\",\n    \"chat.disabled.launcher\": \"Chat disabled by launcher option. Cannot send message\",\n    \"chat.disabled.profile\": \"Chat not allowed by account settings. Cannot send message\",\n    \"disconnect.unknownHost\": \"Unknown host\",\n    \"disconnect.loginFailedInfo.insufficientPrivileges\": \"Multiplayer is disabled. Please check your Microsoft account settings.\",\n    \"options.accessibility.link\": \"Accessibility Guide\",\n    \"options.hideMatchedNames\": \"Hide Matched Names\",\n    \"options.hideMatchedNames.tooltip\": \"3rd-party Servers may send chat messages in non-standard formats.\\nWith this option on: hidden players will be matched based on chat sender names.\",\n    \"options.darkMojangStudiosBackgroundColor\": \"Monochrome Logo\",\n    \"options.darkMojangStudiosBackgroundColor.tooltip\": \"Changes the Mojang Studios loading screen background color to black.\",\n    \"key.socialInteractions\": \"Social Interactions Screen\",\n    \"resourcePack.vanilla.description\": \"The default resources for Minecraft\",\n    \"dataPack.vanilla.description\": \"The default data for Minecraft\",\n    \"block.minecraft.light\": \"Light\",\n    \"block.minecraft.deepslate_gold_ore\": \"Deepslate Gold Ore\",\n    \"block.minecraft.deepslate_iron_ore\": \"Deepslate Iron Ore\",\n    \"block.minecraft.deepslate_coal_ore\": \"Deepslate Coal Ore\",\n    \"block.minecraft.deepslate_diamond_ore\": \"Deepslate Diamond Ore\",\n    \"block.minecraft.deepslate_redstone_ore\": \"Deepslate Redstone Ore\",\n    \"block.minecraft.deepslate_lapis_ore\": \"Deepslate Lapis Lazuli Ore\",\n    \"block.minecraft.water_cauldron\": \"Water Cauldron\",\n    \"block.minecraft.lava_cauldron\": \"Lava Cauldron\",\n    \"block.minecraft.powder_snow_cauldron\": \"Powder Snow Cauldron\",\n    \"block.minecraft.deepslate_emerald_ore\": \"Deepslate Emerald Ore\",\n    \"block.minecraft.dirt_path\": \"Dirt Path\",\n    \"block.minecraft.potted_azalea_bush\": \"Potted Azalea\",\n    \"block.minecraft.potted_flowering_azalea_bush\": \"Potted Flowering Azalea\",\n    \"block.minecraft.candle\": \"Candle\",\n    \"block.minecraft.white_candle\": \"White Candle\",\n    \"block.minecraft.orange_candle\": \"Orange Candle\",\n    \"block.minecraft.magenta_candle\": \"Magenta Candle\",\n    \"block.minecraft.light_blue_candle\": \"Light Blue Candle\",\n    \"block.minecraft.yellow_candle\": \"Yellow Candle\",\n    \"block.minecraft.lime_candle\": \"Lime Candle\",\n    \"block.minecraft.pink_candle\": \"Pink Candle\",\n    \"block.minecraft.gray_candle\": \"Gray Candle\",\n    \"block.minecraft.light_gray_candle\": \"Light Gray Candle\",\n    \"block.minecraft.cyan_candle\": \"Cyan Candle\",\n    \"block.minecraft.purple_candle\": \"Purple Candle\",\n    \"block.minecraft.blue_candle\": \"Blue Candle\",\n    \"block.minecraft.brown_candle\": \"Brown Candle\",\n    \"block.minecraft.green_candle\": \"Green Candle\",\n    \"block.minecraft.red_candle\": \"Red Candle\",\n    \"block.minecraft.black_candle\": \"Black Candle\",\n    \"block.minecraft.candle_cake\": \"Cake with Candle\",\n    \"block.minecraft.white_candle_cake\": \"Cake with White Candle\",\n    \"block.minecraft.orange_candle_cake\": \"Cake with Orange Candle\",\n    \"block.minecraft.magenta_candle_cake\": \"Cake with Magenta Candle\",\n    \"block.minecraft.light_blue_candle_cake\": \"Cake with Light Blue Candle\",\n    \"block.minecraft.yellow_candle_cake\": \"Cake with Yellow Candle\",\n    \"block.minecraft.lime_candle_cake\": \"Cake with Lime Candle\",\n    \"block.minecraft.pink_candle_cake\": \"Cake with Pink Candle\",\n    \"block.minecraft.gray_candle_cake\": \"Cake with Gray Candle\",\n    \"block.minecraft.light_gray_candle_cake\": \"Cake with Light Gray Candle\",\n    \"block.minecraft.cyan_candle_cake\": \"Cake with Cyan Candle\",\n    \"block.minecraft.purple_candle_cake\": \"Cake with Purple Candle\",\n    \"block.minecraft.blue_candle_cake\": \"Cake with Blue Candle\",\n    \"block.minecraft.brown_candle_cake\": \"Cake with Brown Candle\",\n    \"block.minecraft.green_candle_cake\": \"Cake with Green Candle\",\n    \"block.minecraft.red_candle_cake\": \"Cake with Red Candle\",\n    \"block.minecraft.black_candle_cake\": \"Cake with Black Candle\",\n    \"block.minecraft.amethyst_block\": \"Block of Amethyst\",\n    \"block.minecraft.small_amethyst_bud\": \"Small Amethyst Bud\",\n    \"block.minecraft.medium_amethyst_bud\": \"Medium Amethyst Bud\",\n    \"block.minecraft.large_amethyst_bud\": \"Large Amethyst Bud\",\n    \"block.minecraft.amethyst_cluster\": \"Amethyst Cluster\",\n    \"block.minecraft.budding_amethyst\": \"Budding Amethyst\",\n    \"block.minecraft.calcite\": \"Calcite\",\n    \"block.minecraft.tuff\": \"Tuff\",\n    \"block.minecraft.tinted_glass\": \"Tinted Glass\",\n    \"block.minecraft.dripstone_block\": \"Dripstone Block\",\n    \"block.minecraft.pointed_dripstone\": \"Pointed Dripstone\",\n    \"block.minecraft.copper_ore\": \"Copper Ore\",\n    \"block.minecraft.deepslate_copper_ore\": \"Deepslate Copper Ore\",\n    \"block.minecraft.copper_block\": \"Block of Copper\",\n    \"block.minecraft.exposed_copper\": \"Exposed Copper\",\n    \"block.minecraft.weathered_copper\": \"Weathered Copper\",\n    \"block.minecraft.oxidized_copper\": \"Oxidized Copper\",\n    \"block.minecraft.cut_copper\": \"Cut Copper\",\n    \"block.minecraft.exposed_cut_copper\": \"Exposed Cut Copper\",\n    \"block.minecraft.weathered_cut_copper\": \"Weathered Cut Copper\",\n    \"block.minecraft.oxidized_cut_copper\": \"Oxidized Cut Copper\",\n    \"block.minecraft.cut_copper_stairs\": \"Cut Copper Stairs\",\n    \"block.minecraft.exposed_cut_copper_stairs\": \"Exposed Cut Copper Stairs\",\n    \"block.minecraft.weathered_cut_copper_stairs\": \"Weathered Cut Copper Stairs\",\n    \"block.minecraft.oxidized_cut_copper_stairs\": \"Oxidized Cut Copper Stairs\",\n    \"block.minecraft.cut_copper_slab\": \"Cut Copper Slab\",\n    \"block.minecraft.exposed_cut_copper_slab\": \"Exposed Cut Copper Slab\",\n    \"block.minecraft.weathered_cut_copper_slab\": \"Weathered Cut Copper Slab\",\n    \"block.minecraft.oxidized_cut_copper_slab\": \"Oxidized Cut Copper Slab\",\n    \"block.minecraft.waxed_copper_block\": \"Waxed Block of Copper\",\n    \"block.minecraft.waxed_exposed_copper\": \"Waxed Exposed Copper\",\n    \"block.minecraft.waxed_weathered_copper\": \"Waxed Weathered Copper\",\n    \"block.minecraft.waxed_oxidized_copper\": \"Waxed Oxidized Copper\",\n    \"block.minecraft.waxed_cut_copper\": \"Waxed Cut Copper\",\n    \"block.minecraft.waxed_exposed_cut_copper\": \"Waxed Exposed Cut Copper\",\n    \"block.minecraft.waxed_weathered_cut_copper\": \"Waxed Weathered Cut Copper\",\n    \"block.minecraft.waxed_oxidized_cut_copper\": \"Waxed Oxidized Cut Copper\",\n    \"block.minecraft.waxed_cut_copper_stairs\": \"Waxed Cut Copper Stairs\",\n    \"block.minecraft.waxed_exposed_cut_copper_stairs\": \"Waxed Exposed Cut Copper Stairs\",\n    \"block.minecraft.waxed_weathered_cut_copper_stairs\": \"Waxed Weathered Cut Copper Stairs\",\n    \"block.minecraft.waxed_oxidized_cut_copper_stairs\": \"Waxed Oxidized Cut Copper Stairs\",\n    \"block.minecraft.waxed_cut_copper_slab\": \"Waxed Cut Copper Slab\",\n    \"block.minecraft.waxed_exposed_cut_copper_slab\": \"Waxed Exposed Cut Copper Slab\",\n    \"block.minecraft.waxed_weathered_cut_copper_slab\": \"Waxed Weathered Cut Copper Slab\",\n    \"block.minecraft.waxed_oxidized_cut_copper_slab\": \"Waxed Oxidized Cut Copper Slab\",\n    \"block.minecraft.lightning_rod\": \"Lightning Rod\",\n    \"block.minecraft.cave_vines\": \"Cave Vines\",\n    \"block.minecraft.cave_vines_plant\": \"Cave Vines Plant\",\n    \"block.minecraft.spore_blossom\": \"Spore Blossom\",\n    \"block.minecraft.azalea\": \"Azalea\",\n    \"block.minecraft.flowering_azalea\": \"Flowering Azalea\",\n    \"block.minecraft.azalea_leaves\": \"Azalea Leaves\",\n    \"block.minecraft.flowering_azalea_leaves\": \"Flowering Azalea Leaves\",\n    \"block.minecraft.moss_carpet\": \"Moss Carpet\",\n    \"block.minecraft.moss_block\": \"Moss Block\",\n    \"block.minecraft.big_dripleaf\": \"Big Dripleaf\",\n    \"block.minecraft.big_dripleaf_stem\": \"Big Dripleaf Stem\",\n    \"block.minecraft.small_dripleaf\": \"Small Dripleaf\",\n    \"block.minecraft.rooted_dirt\": \"Rooted Dirt\",\n    \"block.minecraft.hanging_roots\": \"Hanging Roots\",\n    \"block.minecraft.powder_snow\": \"Powder Snow\",\n    \"block.minecraft.glow_lichen\": \"Glow Lichen\",\n    \"block.minecraft.sculk_sensor\": \"Sculk Sensor\",\n    \"block.minecraft.deepslate\": \"Deepslate\",\n    \"block.minecraft.cobbled_deepslate\": \"Cobbled Deepslate\",\n    \"block.minecraft.cobbled_deepslate_slab\": \"Cobbled Deepslate Slab\",\n    \"block.minecraft.cobbled_deepslate_stairs\": \"Cobbled Deepslate Stairs\",\n    \"block.minecraft.cobbled_deepslate_wall\": \"Cobbled Deepslate Wall\",\n    \"block.minecraft.chiseled_deepslate\": \"Chiseled Deepslate\",\n    \"block.minecraft.polished_deepslate\": \"Polished Deepslate\",\n    \"block.minecraft.polished_deepslate_slab\": \"Polished Deepslate Slab\",\n    \"block.minecraft.polished_deepslate_stairs\": \"Polished Deepslate Stairs\",\n    \"block.minecraft.polished_deepslate_wall\": \"Polished Deepslate Wall\",\n    \"block.minecraft.deepslate_bricks\": \"Deepslate Bricks\",\n    \"block.minecraft.deepslate_brick_slab\": \"Deepslate Brick Slab\",\n    \"block.minecraft.deepslate_brick_stairs\": \"Deepslate Brick Stairs\",\n    \"block.minecraft.deepslate_brick_wall\": \"Deepslate Brick Wall\",\n    \"block.minecraft.deepslate_tiles\": \"Deepslate Tiles\",\n    \"block.minecraft.deepslate_tile_slab\": \"Deepslate Tile Slab\",\n    \"block.minecraft.deepslate_tile_stairs\": \"Deepslate Tile Stairs\",\n    \"block.minecraft.deepslate_tile_wall\": \"Deepslate Tile Wall\",\n    \"block.minecraft.cracked_deepslate_bricks\": \"Cracked Deepslate Bricks\",\n    \"block.minecraft.cracked_deepslate_tiles\": \"Cracked Deepslate Tiles\",\n    \"block.minecraft.infested_deepslate\": \"Infested Deepslate\",\n    \"block.minecraft.smooth_basalt\": \"Smooth Basalt\",\n    \"block.minecraft.raw_iron_block\": \"Block of Raw Iron\",\n    \"block.minecraft.raw_copper_block\": \"Block of Raw Copper\",\n    \"block.minecraft.raw_gold_block\": \"Block of Raw Gold\",\n    \"item.minecraft.bundle\": \"Bundle\",\n    \"item.minecraft.bundle.fullness\": \"%s/%s\",\n    \"item.minecraft.raw_copper\": \"Raw Copper\",\n    \"item.minecraft.raw_iron\": \"Raw Iron\",\n    \"item.minecraft.raw_gold\": \"Raw Gold\",\n    \"item.minecraft.copper_ingot\": \"Copper Ingot\",\n    \"item.minecraft.powder_snow_bucket\": \"Powder Snow Bucket\",\n    \"item.minecraft.axolotl_bucket\": \"Bucket of Axolotl\",\n    \"item.minecraft.amethyst_shard\": \"Amethyst Shard\",\n    \"item.minecraft.spyglass\": \"Spyglass\",\n    \"item.minecraft.glow_berries\": \"Glow Berries\",\n    \"item.minecraft.axolotl_spawn_egg\": \"Axolotl Spawn Egg\",\n    \"item.minecraft.glow_squid_spawn_egg\": \"Glow Squid Spawn Egg\",\n    \"item.minecraft.goat_spawn_egg\": \"Goat Spawn Egg\",\n    \"item.minecraft.glow_ink_sac\": \"Glow Ink Sac\",\n    \"item.minecraft.glow_item_frame\": \"Glow Item Frame\",\n    \"entity.minecraft.axolotl\": \"Axolotl\",\n    \"entity.minecraft.glow_item_frame\": \"Glow Item Frame\",\n    \"entity.minecraft.glow_squid\": \"Glow Squid\",\n    \"entity.minecraft.goat\": \"Goat\",\n    \"entity.minecraft.marker\": \"Marker\",\n    \"death.attack.stalagmite\": \"%1$s was impaled on a stalagmite\",\n    \"death.attack.stalagmite.player\": \"%1$s was impaled on a stalagmite whilst fighting %2$s\",\n    \"death.attack.fallingStalactite\": \"%1$s was skewered by a falling stalactite\",\n    \"death.attack.fallingStalactite.player\": \"%1$s was skewered by a falling stalactite whilst fighting %2$s\",\n    \"death.attack.freeze\": \"%1$s froze to death\",\n    \"death.attack.freeze.player\": \"%1$s was frozen to death by %2$s\",\n    \"stat.minecraft.play_time\": \"Time Played\",\n    \"stat.minecraft.total_world_time\": \"Time with World Open\",\n    \"advMode.mode\": \"Mode\",\n    \"advMode.type\": \"Type\",\n    \"advMode.triggering\": \"Triggering\",\n    \"advMode.trackOutput\": \"Track output\",\n    \"block.minecraft.banner.base.black\": \"Fully Black Field\",\n    \"block.minecraft.banner.base.red\": \"Fully Red Field\",\n    \"block.minecraft.banner.base.green\": \"Fully Green Field\",\n    \"block.minecraft.banner.base.brown\": \"Fully Brown Field\",\n    \"block.minecraft.banner.base.blue\": \"Fully Blue Field\",\n    \"block.minecraft.banner.base.purple\": \"Fully Purple Field\",\n    \"block.minecraft.banner.base.cyan\": \"Fully Cyan Field\",\n    \"block.minecraft.banner.base.light_gray\": \"Fully Light Gray Field\",\n    \"block.minecraft.banner.base.gray\": \"Fully Gray Field\",\n    \"block.minecraft.banner.base.pink\": \"Fully Pink Field\",\n    \"block.minecraft.banner.base.lime\": \"Fully Lime Field\",\n    \"block.minecraft.banner.base.yellow\": \"Fully Yellow Field\",\n    \"block.minecraft.banner.base.light_blue\": \"Fully Light Blue Field\",\n    \"block.minecraft.banner.base.magenta\": \"Fully Magenta Field\",\n    \"block.minecraft.banner.base.orange\": \"Fully Orange Field\",\n    \"block.minecraft.banner.base.white\": \"Fully White Field\",\n    \"subtitles.block.amethyst_block.chime\": \"Amethyst chimes\",\n    \"subtitles.block.big_dripleaf.tilt_down\": \"Dripleaf tilts down\",\n    \"subtitles.block.big_dripleaf.tilt_up\": \"Dripleaf tilts up\",\n    \"subtitles.block.candle.crackle\": \"Candle crackles\",\n    \"subtitles.block.cake.add_candle\": \"Cake squishes\",\n    \"subtitles.item.honeycomb.wax_on\": \"Wax on\",\n    \"subtitles.block.pointed_dripstone.land\": \"Stalactite crashes down\",\n    \"subtitles.block.pointed_dripstone.drip_lava\": \"Lava drips\",\n    \"subtitles.block.pointed_dripstone.drip_water\": \"Water drips\",\n    \"subtitles.block.pointed_dripstone.drip_lava_into_cauldron\": \"Lava drips into Cauldron\",\n    \"subtitles.block.pointed_dripstone.drip_water_into_cauldron\": \"Water drips into Cauldron\",\n    \"subtitles.block.sculk_sensor.clicking\": \"Sculk Sensor starts clicking\",\n    \"subtitles.block.sculk_sensor.clicking_stop\": \"Sculk Sensor stops clicking\",\n    \"subtitles.block.sweet_berry_bush.pick_berries\": \"Berries pop\",\n    \"subtitles.entity.axolotl.attack\": \"Axolotl attacks\",\n    \"subtitles.entity.axolotl.death\": \"Axolotl dies\",\n    \"subtitles.entity.axolotl.hurt\": \"Axolotl hurts\",\n    \"subtitles.entity.axolotl.idle_air\": \"Axolotl chirps\",\n    \"subtitles.entity.axolotl.idle_water\": \"Axolotl chirps\",\n    \"subtitles.entity.axolotl.splash\": \"Axolotl splashes\",\n    \"subtitles.entity.axolotl.swim\": \"Axolotl swims\",\n    \"subtitles.entity.glow_item_frame.add_item\": \"Glow Item Frame fills\",\n    \"subtitles.entity.glow_item_frame.break\": \"Glow Item Frame breaks\",\n    \"subtitles.entity.glow_item_frame.place\": \"Glow Item Frame placed\",\n    \"subtitles.entity.glow_item_frame.remove_item\": \"Glow Item Frame empties\",\n    \"subtitles.entity.glow_item_frame.rotate_item\": \"Glow Item Frame clicks\",\n    \"subtitles.entity.glow_squid.ambient\": \"Glow Squid swims\",\n    \"subtitles.entity.glow_squid.death\": \"Glow Squid dies\",\n    \"subtitles.entity.glow_squid.hurt\": \"Glow Squid hurts\",\n    \"subtitles.entity.glow_squid.squirt\": \"Glow Squid shoots ink\",\n    \"subtitles.entity.goat.ambient\": \"Goat bleats\",\n    \"subtitles.entity.goat.screaming.ambient\": \"Goat bellows\",\n    \"subtitles.entity.goat.death\": \"Goat dies\",\n    \"subtitles.entity.goat.eat\": \"Goat eats\",\n    \"subtitles.entity.goat.hurt\": \"Goat hurts\",\n    \"subtitles.entity.goat.long_jump\": \"Goat leaps\",\n    \"subtitles.entity.goat.milk\": \"Goat gets milked\",\n    \"subtitles.entity.goat.prepare_ram\": \"Goat stomps\",\n    \"subtitles.entity.goat.ram_impact\": \"Goat rams\",\n    \"subtitles.entity.goat.step\": \"Goat steps\",\n    \"subtitles.entity.player.freeze_hurt\": \"Player freezes\",\n    \"subtitles.entity.skeleton.converted_to_stray\": \"Skeleton converts to Stray\",\n    \"subtitles.item.axe.scrape\": \"Axe scrapes\",\n    \"subtitles.item.axe.wax_off\": \"Wax off\",\n    \"subtitles.item.bone_meal.use\": \"Bone Meal crinkles\",\n    \"subtitles.item.bucket.fill_axolotl\": \"Axolotl scooped\",\n    \"subtitles.item.spyglass.use\": \"Spyglass expands\",\n    \"subtitles.item.spyglass.stop_using\": \"Spyglass retracts\",\n    \"subtitles.item.ink_sac.use\": \"Ink Sac splotches\",\n    \"subtitles.item.glow_ink_sac.use\": \"Glow Ink Sac splotches\",\n    \"subtitles.item.dye.use\": \"Dye stains\",\n    \"debug.profiling.help\": \"F3 + L = Start/stop profiling\",\n    \"debug.profiling.start\": \"Profiling started for %s seconds. Use F3 + L to stop early\",\n    \"debug.profiling.stop\": \"Profiling ended. Saved results to %s\",\n    \"tutorial.bundleInsert.title\": \"Use a Bundle\",\n    \"tutorial.bundleInsert.description\": \"Right Click to add items\",\n    \"tutorial.socialInteractions.title\": \"Social Interactions\",\n    \"tutorial.socialInteractions.description\": \"Press %s to open\",\n    \"advancements.adventure.walk_on_powder_snow_with_leather_boots.title\": \"Light as a Rabbit\",\n    \"advancements.adventure.walk_on_powder_snow_with_leather_boots.description\": \"Walk on powder snow...without sinking in it\",\n    \"advancements.adventure.lightning_rod_with_villager_no_fire.title\": \"Surge Protector\",\n    \"advancements.adventure.lightning_rod_with_villager_no_fire.description\": \"Protect a villager from an undesired shock without starting a fire\",\n    \"advancements.adventure.spyglass_at_parrot.title\": \"Is It a Bird?\",\n    \"advancements.adventure.spyglass_at_parrot.description\": \"Look at a parrot through a spyglass\",\n    \"advancements.adventure.spyglass_at_ghast.title\": \"Is It a Balloon?\",\n    \"advancements.adventure.spyglass_at_ghast.description\": \"Look at a ghast through a spyglass\",\n    \"advancements.adventure.spyglass_at_dragon.title\": \"Is It a Plane?\",\n    \"advancements.adventure.spyglass_at_dragon.description\": \"Look at the Ender Dragon through a spyglass\",\n    \"advancements.husbandry.make_a_sign_glow.title\": \"Glow and Behold!\",\n    \"advancements.husbandry.make_a_sign_glow.description\": \"Make the text of a sign glow\",\n    \"advancements.husbandry.ride_a_boat_with_a_goat.title\": \"Whatever Floats Your Goat!\",\n    \"advancements.husbandry.ride_a_boat_with_a_goat.description\": \"Get in a Boat and float with a Goat\",\n    \"advancements.husbandry.axolotl_in_a_bucket.title\": \"The Cutest Predator\",\n    \"advancements.husbandry.axolotl_in_a_bucket.description\": \"Catch an axolotl in a bucket\",\n    \"advancements.husbandry.kill_axolotl_target.title\": \"The Healing Power of Friendship!\",\n    \"advancements.husbandry.kill_axolotl_target.description\": \"Team up with an axolotl and win a fight\",\n    \"advancements.husbandry.wax_on.title\": \"Wax On\",\n    \"advancements.husbandry.wax_on.description\": \"Apply Honeycomb to a Copper block!\",\n    \"advancements.husbandry.wax_off.title\": \"Wax Off\",\n    \"advancements.husbandry.wax_off.description\": \"Scrape Wax off of a Copper block!\",\n    \"commands.debug.function.success.single\": \"Traced %s commands from function '%s' to output file %s\",\n    \"commands.debug.function.success.multiple\": \"Traced %s commands from %s functions to output file %s\",\n    \"commands.debug.function.noRecursion\": \"Can't trace from inside of function\",\n    \"commands.debug.function.traceFailed\": \"Failed to trace function\",\n    \"commands.give.failed.toomanyitems\": \"Can't give more than %s of %s\",\n    \"commands.perf.started\": \"Started 10 second performance profiling run (use '/perf stop' to stop early)\",\n    \"commands.perf.stopped\": \"Stopped performance profiling after %s seconds and %s ticks (%s ticks per second)\",\n    \"commands.perf.reportSaved\": \"Created debug report in %s\",\n    \"commands.perf.reportFailed\": \"Failed to create debug report\",\n    \"commands.perf.notRunning\": \"The performance profiler hasn't started\",\n    \"commands.perf.alreadyRunning\": \"The performance profiler is already started\",\n    \"commands.item.target.not_a_container\": \"Target position %s, %s, %s is not a container\",\n    \"commands.item.source.not_a_container\": \"Source position %s, %s, %s is not a container\",\n    \"commands.item.target.no_such_slot\": \"The target does not have slot %s\",\n    \"commands.item.source.no_such_slot\": \"The source does not have slot %s\",\n    \"commands.item.target.no_changes\": \"No targets accepted item into slot %s\",\n    \"commands.item.target.no_changed.known_item\": \"No targets accepted item %s into slot %s\",\n    \"commands.item.block.set.success\": \"Replaced a slot at %s, %s, %s with %s\",\n    \"commands.item.entity.set.success.single\": \"Replaced a slot on %s with %s\",\n    \"commands.item.entity.set.success.multiple\": \"Replaced a slot on %s entities with %s\",\n    \"argument.angle.invalid\": \"Invalid angle\",\n    \"argument.pos.outofbounds\": \"That position is outside the allowed boundaries.\",\n    \"commands.worldborder.set.failed.small\": \"World border cannot be smaller than 1 block wide\",\n    \"commands.worldborder.set.failed.big\": \"World border cannot be bigger than %s blocks wide\",\n    \"item_modifier.unknown\": \"Unknown item modifier: %s\",\n    \"biome.minecraft.dripstone_caves\": \"Dripstone Caves\",\n    \"biome.minecraft.lush_caves\": \"Lush Caves\",\n    \"gamerule.freezeDamage\": \"Deal freeze damage\",\n    \"gamerule.playersSleepingPercentage\": \"Sleep percentage\",\n    \"gamerule.playersSleepingPercentage.description\": \"The percentage of players who must be sleeping to skip the night.\",\n    \"mirror.none\": \"|\",\n    \"mirror.left_right\": \"← →\",\n    \"mirror.front_back\": \"↑ ↓\",\n    \"sleep.not_possible\": \"No amount of rest can pass this night\",\n    \"sleep.players_sleeping\": \"%s/%s players sleeping\",\n    \"sleep.skipping_night\": \"Sleeping through this night\"\n  },\n  \"1.16.2\": {\n    \"multiplayer.disconnect.missing_tags\": \"Incomplete set of tags received from server.\\nPlease contact server operator.\",\n    \"chat.square_brackets\": \"[%s]\",\n    \"disconnect.exceeded_packet_rate\": \"Kicked for exceeding packet rate limit\",\n    \"item.minecraft.piglin_brute_spawn_egg\": \"Piglin Brute Spawn Egg\",\n    \"entity.minecraft.piglin_brute\": \"Piglin Brute\",\n    \"potion.withAmplifier\": \"%s %s\",\n    \"potion.withDuration\": \"%s (%s)\",\n    \"advancements.sad_label\": \":(\",\n    \"subtitles.entity.parrot.imitate.piglin_brute\": \"Parrot snorts mightily\",\n    \"subtitles.entity.piglin_brute.ambient\": \"Piglin Brute snorts\",\n    \"subtitles.entity.piglin_brute.angry\": \"Piglin Brute snorts angrily\",\n    \"subtitles.entity.piglin_brute.death\": \"Piglin Brute dies\",\n    \"subtitles.entity.piglin_brute.hurt\": \"Piglin Brute hurts\",\n    \"subtitles.entity.piglin_brute.step\": \"Piglin Brute steps\",\n    \"subtitles.entity.piglin_brute.converted_to_zombified\": \"Piglin Brute converts to Zombified Piglin\",\n    \"command.context.parse_error\": \"%s at position %s: %s\",\n    \"commands.list.nameAndId\": \"%s (%s)\",\n    \"argument.angle.incomplete\": \"Incomplete (expected 1 angle)\",\n    \"commands.summon.failed.uuid\": \"Unable to summon entity due to duplicate UUIDs\"\n  },\n  \"1.16\": {\n    \"gui.recipebook.search_hint\": \"Search...\",\n    \"selectWorld.edit.export_worldgen_settings\": \"Export World Generation Settings\",\n    \"selectWorld.edit.export_worldgen_settings.success\": \"Exported\",\n    \"selectWorld.edit.export_worldgen_settings.failure\": \"Export failed\",\n    \"selectWorld.locked\": \"Locked by another running instance of Minecraft\",\n    \"selectWorld.backupQuestion.experimental\": \"Worlds using Experimental Settings are not supported\",\n    \"selectWorld.backupWarning.experimental\": \"This world uses experimental settings that could stop working at any time. We cannot guarantee it will load or work. Here be dragons!\",\n    \"selectWorld.access_failure\": \"Failed to access world\",\n    \"selectWorld.delete_failure\": \"Failed to delete world\",\n    \"selectWorld.data_read\": \"Reading world data...\",\n    \"createWorld.preparing\": \"Preparing for world creation...\",\n    \"datapackFailure.title\": \"Errors in currently selected datapacks prevented world from loading.\\nYou can either try to load only with vanilla datapack (\\\"safe mode\\\") or go back to title screen and fix it manually.\",\n    \"datapackFailure.safeMode\": \"Safe Mode\",\n    \"editGamerule.title\": \"Edit Game Rules\",\n    \"editGamerule.default\": \"Default: %s\",\n    \"selectWorld.gameRules\": \"Game Rules\",\n    \"selectWorld.dataPacks\": \"Data Packs\",\n    \"selectWorld.import_worldgen_settings\": \"Import Settings\",\n    \"selectWorld.import_worldgen_settings.select_file\": \"Select settings file (.json)\",\n    \"selectWorld.import_worldgen_settings.failure\": \"Error importing settings\",\n    \"selectWorld.import_worldgen_settings.experimental.title\": \"Warning! These settings are using experimental features\",\n    \"selectWorld.import_worldgen_settings.experimental.question\": \"These settings are experimental and could one day stop working. Do you wish to proceed?\",\n    \"selectWorld.import_worldgen_settings.deprecated.title\": \"Warning! These settings are using deprecated features\",\n    \"selectWorld.import_worldgen_settings.deprecated.question\": \"Some features used are deprecated and will stop working in the future. Do you wish to proceed?\",\n    \"generator.large_biomes\": \"Large Biomes\",\n    \"generator.custom\": \"Custom\",\n    \"generator.single_biome_surface\": \"Single Biome\",\n    \"generator.single_biome_caves\": \"Caves\",\n    \"generator.single_biome_floating_islands\": \"Floating Islands\",\n    \"multiplayer.status.ping\": \"%s ms\",\n    \"chat.queue\": \"[+%s pending lines]\",\n    \"options.entityDistanceScaling\": \"Entity Distance\",\n    \"options.entityDistancePercent\": \"%s%%\",\n    \"options.graphics.fabulous.tooltip\": \"%s graphics uses screen shaders for drawing weather, clouds and particles behind translucent blocks and water.\\nThis may severely impact performance for portable devices and 4K displays.\",\n    \"options.graphics.fabulous\": \"Fabulous!\",\n    \"options.graphics.fancy.tooltip\": \"Fancy graphics balances performance and quality for the majority of machines.\\nWeather, clouds and particles may not appear behind translucent blocks or water.\",\n    \"options.graphics.fast.tooltip\": \"Fast graphics reduces the amount of visible rain and snow.\\nTransparency effects are disabled for various blocks such as tree-leaves.\",\n    \"options.graphics.warning.title\": \"Graphics Device Unsupported\",\n    \"options.graphics.warning.message\": \"Your graphics device is detected as unsupported for the %s graphics option.\\n\\nYou may ignore this and continue, however support will not be provided for your device if you choose to use %s graphics.\",\n    \"options.graphics.warning.renderer\": \"Renderer detected: [%s]\",\n    \"options.graphics.warning.vendor\": \"Vendor detected: [%s]\",\n    \"options.graphics.warning.version\": \"OpenGL Version detected: [%s]\",\n    \"options.graphics.warning.accept\": \"Continue without support\",\n    \"options.graphics.warning.cancel\": \"Take me back\",\n    \"options.chat.line_spacing\": \"Line Spacing\",\n    \"options.chat.delay_none\": \"Chat Delay: None\",\n    \"options.chat.delay\": \"Chat Delay: %s seconds\",\n    \"title.multiplayer.disabled\": \"Multiplayer is disabled, please check your launcher settings.\",\n    \"key.swapOffhand\": \"Swap Item With Offhand\",\n    \"pack.available.title\": \"Available\",\n    \"pack.selected.title\": \"Selected\",\n    \"pack.incompatible\": \"Incompatible\",\n    \"pack.incompatible.old\": \"(Made for an older version of Minecraft)\",\n    \"pack.incompatible.new\": \"(Made for a newer version of Minecraft)\",\n    \"pack.incompatible.confirm.title\": \"Are you sure you want to load this pack?\",\n    \"pack.incompatible.confirm.old\": \"This pack was made for an older version of Minecraft and may no longer work correctly.\",\n    \"pack.incompatible.confirm.new\": \"This pack was made for a newer version of Minecraft and may no longer work correctly.\",\n    \"pack.dropInfo\": \"Drag and drop files into this window to add packs\",\n    \"pack.dropConfirm\": \"Do you want to add following packs to Minecraft?\",\n    \"pack.copyFailure\": \"Failed to copy packs\",\n    \"pack.nameAndSource\": \"%s (%s)\",\n    \"pack.openFolder\": \"Open Pack Folder\",\n    \"pack.folderInfo\": \"(Place pack files here)\",\n    \"dataPack.title\": \"Select Data Packs\",\n    \"dataPack.validation.working\": \"Validating selected data packs...\",\n    \"dataPack.validation.failed\": \"Data pack validation failed!\",\n    \"dataPack.validation.back\": \"Go Back\",\n    \"dataPack.validation.reset\": \"Reset to Default\",\n    \"block.minecraft.nether_gold_ore\": \"Nether Gold Ore\",\n    \"block.minecraft.soul_torch\": \"Soul Torch\",\n    \"block.minecraft.soul_wall_torch\": \"Soul Wall Torch\",\n    \"block.minecraft.respawn_anchor\": \"Respawn Anchor\",\n    \"block.minecraft.spawn.not_valid\": \"You have no home bed or charged respawn anchor, or it was obstructed\",\n    \"block.minecraft.set_spawn\": \"Respawn point set\",\n    \"block.minecraft.warped_wart_block\": \"Warped Wart Block\",\n    \"block.minecraft.warped_stem\": \"Warped Stem\",\n    \"block.minecraft.stripped_warped_stem\": \"Stripped Warped Stem\",\n    \"block.minecraft.warped_hyphae\": \"Warped Hyphae\",\n    \"block.minecraft.stripped_warped_hyphae\": \"Stripped Warped Hyphae\",\n    \"block.minecraft.crimson_stem\": \"Crimson Stem\",\n    \"block.minecraft.stripped_crimson_stem\": \"Stripped Crimson Stem\",\n    \"block.minecraft.crimson_hyphae\": \"Crimson Hyphae\",\n    \"block.minecraft.stripped_crimson_hyphae\": \"Stripped Crimson Hyphae\",\n    \"block.minecraft.warped_nylium\": \"Warped Nylium\",\n    \"block.minecraft.crimson_nylium\": \"Crimson Nylium\",\n    \"block.minecraft.warped_fungus\": \"Warped Fungus\",\n    \"block.minecraft.crimson_fungus\": \"Crimson Fungus\",\n    \"block.minecraft.crimson_roots\": \"Crimson Roots\",\n    \"block.minecraft.warped_roots\": \"Warped Roots\",\n    \"block.minecraft.nether_sprouts\": \"Nether Sprouts\",\n    \"block.minecraft.shroomlight\": \"Shroomlight\",\n    \"block.minecraft.weeping_vines\": \"Weeping Vines\",\n    \"block.minecraft.weeping_vines_plant\": \"Weeping Vines Plant\",\n    \"block.minecraft.twisting_vines\": \"Twisting Vines\",\n    \"block.minecraft.twisting_vines_plant\": \"Twisting Vines Plant\",\n    \"block.minecraft.soul_soil\": \"Soul Soil\",\n    \"block.minecraft.basalt\": \"Basalt\",\n    \"block.minecraft.polished_basalt\": \"Polished Basalt\",\n    \"block.minecraft.warped_planks\": \"Warped Planks\",\n    \"block.minecraft.warped_slab\": \"Warped Slab\",\n    \"block.minecraft.warped_pressure_plate\": \"Warped Pressure Plate\",\n    \"block.minecraft.warped_fence\": \"Warped Fence\",\n    \"block.minecraft.warped_trapdoor\": \"Warped Trapdoor\",\n    \"block.minecraft.warped_fence_gate\": \"Warped Fence Gate\",\n    \"block.minecraft.warped_stairs\": \"Warped Stairs\",\n    \"block.minecraft.warped_button\": \"Warped Button\",\n    \"block.minecraft.warped_door\": \"Warped Door\",\n    \"block.minecraft.warped_sign\": \"Warped Sign\",\n    \"block.minecraft.warped_wall_sign\": \"Warped Wall Sign\",\n    \"block.minecraft.crimson_planks\": \"Crimson Planks\",\n    \"block.minecraft.crimson_slab\": \"Crimson Slab\",\n    \"block.minecraft.crimson_pressure_plate\": \"Crimson Pressure Plate\",\n    \"block.minecraft.crimson_fence\": \"Crimson Fence\",\n    \"block.minecraft.crimson_trapdoor\": \"Crimson Trapdoor\",\n    \"block.minecraft.crimson_fence_gate\": \"Crimson Fence Gate\",\n    \"block.minecraft.crimson_stairs\": \"Crimson Stairs\",\n    \"block.minecraft.crimson_button\": \"Crimson Button\",\n    \"block.minecraft.crimson_door\": \"Crimson Door\",\n    \"block.minecraft.crimson_sign\": \"Crimson Sign\",\n    \"block.minecraft.crimson_wall_sign\": \"Crimson Wall Sign\",\n    \"block.minecraft.soul_fire\": \"Soul Fire\",\n    \"block.minecraft.potted_crimson_fungus\": \"Potted Crimson Fungus\",\n    \"block.minecraft.potted_warped_fungus\": \"Potted Warped Fungus\",\n    \"block.minecraft.potted_crimson_roots\": \"Potted Crimson Roots\",\n    \"block.minecraft.potted_warped_roots\": \"Potted Warped Roots\",\n    \"block.minecraft.target\": \"Target\",\n    \"block.minecraft.soul_lantern\": \"Soul Lantern\",\n    \"block.minecraft.soul_campfire\": \"Soul Campfire\",\n    \"block.minecraft.lodestone\": \"Lodestone\",\n    \"block.minecraft.netherite_block\": \"Block of Netherite\",\n    \"block.minecraft.ancient_debris\": \"Ancient Debris\",\n    \"block.minecraft.crying_obsidian\": \"Crying Obsidian\",\n    \"block.minecraft.blackstone\": \"Blackstone\",\n    \"block.minecraft.blackstone_slab\": \"Blackstone Slab\",\n    \"block.minecraft.blackstone_stairs\": \"Blackstone Stairs\",\n    \"block.minecraft.blackstone_wall\": \"Blackstone Wall\",\n    \"block.minecraft.polished_blackstone_bricks\": \"Polished Blackstone Bricks\",\n    \"block.minecraft.polished_blackstone_brick_slab\": \"Polished Blackstone Brick Slab\",\n    \"block.minecraft.polished_blackstone_brick_stairs\": \"Polished Blackstone Brick Stairs\",\n    \"block.minecraft.polished_blackstone_brick_wall\": \"Polished Blackstone Brick Wall\",\n    \"block.minecraft.chiseled_polished_blackstone\": \"Chiseled Polished Blackstone\",\n    \"block.minecraft.cracked_polished_blackstone_bricks\": \"Cracked Polished Blackstone Bricks\",\n    \"block.minecraft.gilded_blackstone\": \"Gilded Blackstone\",\n    \"block.minecraft.polished_blackstone\": \"Polished Blackstone\",\n    \"block.minecraft.polished_blackstone_wall\": \"Polished Blackstone Wall\",\n    \"block.minecraft.polished_blackstone_slab\": \"Polished Blackstone Slab\",\n    \"block.minecraft.polished_blackstone_stairs\": \"Polished Blackstone Stairs\",\n    \"block.minecraft.polished_blackstone_pressure_plate\": \"Polished Blackstone Pressure Plate\",\n    \"block.minecraft.polished_blackstone_button\": \"Polished Blackstone Button\",\n    \"block.minecraft.cracked_nether_bricks\": \"Cracked Nether Bricks\",\n    \"block.minecraft.chiseled_nether_bricks\": \"Chiseled Nether Bricks\",\n    \"block.minecraft.quartz_bricks\": \"Quartz Bricks\",\n    \"block.minecraft.chain\": \"Chain\",\n    \"item.minecraft.music_disc_pigstep\": \"Music Disc\",\n    \"item.minecraft.music_disc_pigstep.desc\": \"Lena Raine - Pigstep\",\n    \"item.minecraft.hoglin_spawn_egg\": \"Hoglin Spawn Egg\",\n    \"item.minecraft.piglin_spawn_egg\": \"Piglin Spawn Egg\",\n    \"item.minecraft.strider_spawn_egg\": \"Strider Spawn Egg\",\n    \"item.minecraft.zoglin_spawn_egg\": \"Zoglin Spawn Egg\",\n    \"item.minecraft.zombified_piglin_spawn_egg\": \"Zombified Piglin Spawn Egg\",\n    \"item.minecraft.piglin_banner_pattern\": \"Banner Pattern\",\n    \"item.minecraft.piglin_banner_pattern.desc\": \"Snout\",\n    \"item.minecraft.lodestone_compass\": \"Lodestone Compass\",\n    \"item.minecraft.netherite_scrap\": \"Netherite Scrap\",\n    \"item.minecraft.netherite_ingot\": \"Netherite Ingot\",\n    \"item.minecraft.netherite_helmet\": \"Netherite Helmet\",\n    \"item.minecraft.netherite_chestplate\": \"Netherite Chestplate\",\n    \"item.minecraft.netherite_leggings\": \"Netherite Leggings\",\n    \"item.minecraft.netherite_boots\": \"Netherite Boots\",\n    \"item.minecraft.netherite_axe\": \"Netherite Axe\",\n    \"item.minecraft.netherite_pickaxe\": \"Netherite Pickaxe\",\n    \"item.minecraft.netherite_hoe\": \"Netherite Hoe\",\n    \"item.minecraft.netherite_shovel\": \"Netherite Shovel\",\n    \"item.minecraft.netherite_sword\": \"Netherite Sword\",\n    \"item.minecraft.warped_fungus_on_a_stick\": \"Warped Fungus on a Stick\",\n    \"container.upgrade\": \"Upgrade Gear\",\n    \"jigsaw_block.pool\": \"Target pool:\",\n    \"jigsaw_block.name\": \"Name:\",\n    \"jigsaw_block.target\": \"Target name:\",\n    \"jigsaw_block.levels\": \"Levels: %s\",\n    \"jigsaw_block.keep_jigsaws\": \"Keep Jigsaws: \",\n    \"jigsaw_block.generate\": \"Generate\",\n    \"jigsaw_block.joint_label\": \"Joint type:\",\n    \"jigsaw_block.joint.rollable\": \"Rollable\",\n    \"jigsaw_block.joint.aligned\": \"Aligned\",\n    \"entity.minecraft.hoglin\": \"Hoglin\",\n    \"entity.minecraft.piglin\": \"Piglin\",\n    \"entity.minecraft.strider\": \"Strider\",\n    \"entity.minecraft.zoglin\": \"Zoglin\",\n    \"entity.minecraft.zombified_piglin\": \"Zombified Piglin\",\n    \"death.fell.accident.weeping_vines\": \"%1$s fell off some weeping vines\",\n    \"death.fell.accident.twisting_vines\": \"%1$s fell off some twisting vines\",\n    \"death.fell.accident.scaffolding\": \"%1$s fell off scaffolding\",\n    \"death.fell.accident.other_climbable\": \"%1$s fell while climbing\",\n    \"death.attack.magic.player\": \"%1$s was killed by magic whilst trying to escape %2$s\",\n    \"death.attack.witherSkull\": \"%1$s was shot by a %2$s's skull\",\n    \"death.attack.fireworks.item\": \"%1$s went off with a bang due to a firework fired from %3$s by %2$s\",\n    \"death.attack.badRespawnPoint.message\": \"%1$s was killed by %2$s\",\n    \"death.attack.badRespawnPoint.link\": \"Intentional Game Design\",\n    \"enchantment.minecraft.soul_speed\": \"Soul Speed\",\n    \"gui.entity_tooltip.type\": \"Type: %s\",\n    \"stat.minecraft.target_hit\": \"Targets Hit\",\n    \"stat.minecraft.interact_with_smithing_table\": \"Interactions with Smithing Table\",\n    \"stat.minecraft.strider_one_cm\": \"Distance by Strider\",\n    \"attribute.unknown\": \"Unknown attribute\",\n    \"attribute.name.horse.jump_strength\": \"Horse Jump Strength\",\n    \"attribute.name.zombie.spawn_reinforcements\": \"Zombie Reinforcements\",\n    \"attribute.name.generic.max_health\": \"Max Health\",\n    \"attribute.name.generic.follow_range\": \"Mob Follow Range\",\n    \"attribute.name.generic.knockback_resistance\": \"Knockback Resistance\",\n    \"attribute.name.generic.movement_speed\": \"Speed\",\n    \"attribute.name.generic.flying_speed\": \"Flying Speed\",\n    \"attribute.name.generic.attack_damage\": \"Attack Damage\",\n    \"attribute.name.generic.attack_knockback\": \"Attack Knockback\",\n    \"attribute.name.generic.attack_speed\": \"Attack Speed\",\n    \"attribute.name.generic.armor_toughness\": \"Armor Toughness\",\n    \"block.minecraft.banner.piglin.black\": \"Black Snout\",\n    \"block.minecraft.banner.piglin.red\": \"Red Snout\",\n    \"block.minecraft.banner.piglin.green\": \"Green Snout\",\n    \"block.minecraft.banner.piglin.brown\": \"Brown Snout\",\n    \"block.minecraft.banner.piglin.blue\": \"Blue Snout\",\n    \"block.minecraft.banner.piglin.purple\": \"Purple Snout\",\n    \"block.minecraft.banner.piglin.cyan\": \"Cyan Snout\",\n    \"block.minecraft.banner.piglin.light_gray\": \"Light Gray Snout\",\n    \"block.minecraft.banner.piglin.gray\": \"Gray Snout\",\n    \"block.minecraft.banner.piglin.pink\": \"Pink Snout\",\n    \"block.minecraft.banner.piglin.lime\": \"Lime Snout\",\n    \"block.minecraft.banner.piglin.yellow\": \"Yellow Snout\",\n    \"block.minecraft.banner.piglin.light_blue\": \"Light Blue Snout\",\n    \"block.minecraft.banner.piglin.magenta\": \"Magenta Snout\",\n    \"block.minecraft.banner.piglin.orange\": \"Orange Snout\",\n    \"block.minecraft.banner.piglin.white\": \"White Snout\",\n    \"subtitles.block.beacon.activate\": \"Beacon activates\",\n    \"subtitles.block.beacon.ambient\": \"Beacon hums\",\n    \"subtitles.block.beacon.deactivate\": \"Beacon deactivates\",\n    \"subtitles.block.beacon.power_select\": \"Beacon power selected\",\n    \"subtitles.block.composter.empty\": \"Composter emptied\",\n    \"subtitles.block.composter.fill\": \"Composter filled\",\n    \"subtitles.block.composter.ready\": \"Composter composts\",\n    \"subtitles.block.conduit.activate\": \"Conduit activates\",\n    \"subtitles.block.conduit.ambient\": \"Conduit pulses\",\n    \"subtitles.block.conduit.attack.target\": \"Conduit attacks\",\n    \"subtitles.block.conduit.deactivate\": \"Conduit deactivates\",\n    \"subtitles.block.enchantment_table.use\": \"Enchanting Table used\",\n    \"subtitles.block.end_portal.spawn\": \"End Portal opens\",\n    \"subtitles.block.end_portal_frame.fill\": \"Eye of Ender attaches\",\n    \"subtitles.block.portal.travel\": \"Portal noise fades\",\n    \"subtitles.block.portal.trigger\": \"Portal noise intensifies\",\n    \"subtitles.block.pumpkin.carve\": \"Shears carve\",\n    \"subtitles.block.respawn_anchor.ambient\": \"Portal whooshes\",\n    \"subtitles.block.respawn_anchor.charge\": \"Respawn Anchor is charged\",\n    \"subtitles.block.respawn_anchor.deplete\": \"Respawn Anchor depletes\",\n    \"subtitles.block.respawn_anchor.set_spawn\": \"Respawn Anchor sets spawn\",\n    \"subtitles.block.smithing_table.use\": \"Smithing Table used\",\n    \"subtitles.entity.boat.paddle_land\": \"Rowing\",\n    \"subtitles.entity.boat.paddle_water\": \"Rowing\",\n    \"subtitles.entity.cat.beg_for_food\": \"Cat begs\",\n    \"subtitles.entity.cat.eat\": \"Cat eats\",\n    \"subtitles.entity.cat.hiss\": \"Cat hisses\",\n    \"subtitles.entity.cat.purr\": \"Cat purrs\",\n    \"subtitles.entity.donkey.eat\": \"Donkey eats\",\n    \"subtitles.entity.drowned.ambient_water\": \"Drowned gurgles\",\n    \"subtitles.entity.ender_eye.death\": \"Eye of Ender falls\",\n    \"subtitles.entity.fishing_bobber.retrieve\": \"Bobber retrieved\",\n    \"subtitles.entity.fox.teleport\": \"Fox teleports\",\n    \"subtitles.entity.hoglin.ambient\": \"Hoglin growls\",\n    \"subtitles.entity.hoglin.angry\": \"Hoglin growls angrily\",\n    \"subtitles.entity.hoglin.attack\": \"Hoglin attacks\",\n    \"subtitles.entity.hoglin.converted_to_zombified\": \"Hoglin converts to Zoglin\",\n    \"subtitles.entity.hoglin.death\": \"Hoglin dies\",\n    \"subtitles.entity.hoglin.hurt\": \"Hoglin hurts\",\n    \"subtitles.entity.hoglin.retreat\": \"Hoglin retreats\",\n    \"subtitles.entity.hoglin.step\": \"Hoglin steps\",\n    \"subtitles.entity.mule.angry\": \"Mule neighs\",\n    \"subtitles.entity.mule.eat\": \"Mule eats\",\n    \"subtitles.entity.parrot.fly\": \"Parrot flutters\",\n    \"subtitles.entity.parrot.imitate.hoglin\": \"Parrot growls\",\n    \"subtitles.entity.parrot.imitate.piglin\": \"Parrot snorts\",\n    \"subtitles.entity.parrot.imitate.zoglin\": \"Parrot growls\",\n    \"subtitles.entity.piglin.admiring_item\": \"Piglin admires item\",\n    \"subtitles.entity.piglin.ambient\": \"Piglin snorts\",\n    \"subtitles.entity.piglin.angry\": \"Piglin snorts angrily\",\n    \"subtitles.entity.piglin.celebrate\": \"Piglin celebrates\",\n    \"subtitles.entity.piglin.converted_to_zombified\": \"Piglin converts to Zombified Piglin\",\n    \"subtitles.entity.piglin.death\": \"Piglin dies\",\n    \"subtitles.entity.piglin.hurt\": \"Piglin hurts\",\n    \"subtitles.entity.piglin.jealous\": \"Piglin snorts enviously\",\n    \"subtitles.entity.piglin.retreat\": \"Piglin retreats\",\n    \"subtitles.entity.piglin.step\": \"Piglin steps\",\n    \"subtitles.entity.player.attack.crit\": \"Critical attack\",\n    \"subtitles.entity.player.attack.knockback\": \"Knockback attack\",\n    \"subtitles.entity.player.attack.strong\": \"Strong attack\",\n    \"subtitles.entity.player.attack.sweep\": \"Sweeping attack\",\n    \"subtitles.entity.player.attack.weak\": \"Weak attack\",\n    \"subtitles.entity.player.hurt_drown\": \"Player drowning\",\n    \"subtitles.entity.player.hurt_on_fire\": \"Player burns\",\n    \"subtitles.entity.strider.death\": \"Strider dies\",\n    \"subtitles.entity.strider.eat\": \"Strider eats\",\n    \"subtitles.entity.strider.happy\": \"Strider warbles\",\n    \"subtitles.entity.strider.hurt\": \"Strider hurts\",\n    \"subtitles.entity.strider.idle\": \"Strider chirps\",\n    \"subtitles.entity.strider.retreat\": \"Strider retreats\",\n    \"subtitles.entity.tropical_fish.death\": \"Tropical Fish dies\",\n    \"subtitles.entity.tropical_fish.flop\": \"Tropical Fish flops\",\n    \"subtitles.entity.tropical_fish.hurt\": \"Tropical Fish hurts\",\n    \"subtitles.entity.wandering_trader.disappeared\": \"Wandering Trader disappears\",\n    \"subtitles.entity.wandering_trader.drink_milk\": \"Wandering Trader drinks milk\",\n    \"subtitles.entity.wandering_trader.drink_potion\": \"Wandering Trader drinks potion\",\n    \"subtitles.entity.wandering_trader.reappeared\": \"Wandering Trader appears\",\n    \"subtitles.entity.zoglin.ambient\": \"Zoglin growls\",\n    \"subtitles.entity.zoglin.angry\": \"Zoglin growls angrily\",\n    \"subtitles.entity.zoglin.attack\": \"Zoglin attacks\",\n    \"subtitles.entity.zoglin.death\": \"Zoglin dies\",\n    \"subtitles.entity.zoglin.hurt\": \"Zoglin hurts\",\n    \"subtitles.entity.zoglin.step\": \"Zoglin steps\",\n    \"subtitles.entity.zombie.attack_wooden_door\": \"Door shakes\",\n    \"subtitles.entity.zombie.break_wooden_door\": \"Door breaks\",\n    \"subtitles.entity.zombie.destroy_egg\": \"Turtle Egg stomped\",\n    \"subtitles.entity.zombified_piglin.ambient\": \"Zombified Piglin grunts\",\n    \"subtitles.entity.zombified_piglin.angry\": \"Zombified Piglin grunts angrily\",\n    \"subtitles.entity.zombified_piglin.death\": \"Zombified Piglin dies\",\n    \"subtitles.entity.zombified_piglin.hurt\": \"Zombified Piglin hurts\",\n    \"subtitles.item.armor.equip_netherite\": \"Netherite armor clanks\",\n    \"subtitles.item.bottle.empty\": \"Bottle empties\",\n    \"subtitles.item.bucket.fill_fish\": \"Fish captured\",\n    \"subtitles.item.lodestone_compass.lock\": \"Lodestone Compass locks onto Lodestone\",\n    \"subtitles.particle.soul_escape\": \"Soul escapes\",\n    \"subtitles.ui.cartography_table.take_result\": \"Map drawn\",\n    \"subtitles.ui.loom.take_result\": \"Loom used\",\n    \"subtitles.ui.stonecutter.take_result\": \"Stonecutter used\",\n    \"debug.gamemodes.help\": \"F3 + F4 = Open game mode switcher\",\n    \"debug.gamemodes.error\": \"Unable to open game mode switcher, no permission\",\n    \"debug.gamemodes.press_f4\": \"[ F4 ]\",\n    \"debug.gamemodes.select_next\": \"%s Next\",\n    \"advancements.adventure.bullseye.title\": \"Bullseye\",\n    \"advancements.adventure.bullseye.description\": \"Hit the bullseye of a Target block from at least 30 meters away\",\n    \"advancements.husbandry.netherite_hoe.title\": \"Serious Dedication\",\n    \"advancements.husbandry.netherite_hoe.description\": \"Use a Netherite ingot to upgrade a hoe, and then reevaluate your life choices\",\n    \"advancements.nether.obtain_ancient_debris.title\": \"Hidden in the Depths\",\n    \"advancements.nether.obtain_ancient_debris.description\": \"Obtain Ancient Debris\",\n    \"advancements.nether.netherite_armor.title\": \"Cover Me in Debris\",\n    \"advancements.nether.netherite_armor.description\": \"Get a full suit of Netherite armor\",\n    \"advancements.nether.use_lodestone.title\": \"Country Lode, Take Me Home\",\n    \"advancements.nether.use_lodestone.description\": \"Use a Compass on a Lodestone\",\n    \"advancements.nether.obtain_crying_obsidian.title\": \"Who is Cutting Onions?\",\n    \"advancements.nether.obtain_crying_obsidian.description\": \"Obtain Crying Obsidian\",\n    \"advancements.nether.charge_respawn_anchor.title\": \"Not Quite \\\"Nine\\\" Lives\",\n    \"advancements.nether.charge_respawn_anchor.description\": \"Charge a Respawn Anchor to the maximum\",\n    \"advancements.nether.ride_strider.title\": \"This Boat Has Legs\",\n    \"advancements.nether.ride_strider.description\": \"Ride a Strider with a Warped Fungus on a Stick\",\n    \"advancements.nether.explore_nether.title\": \"Hot Tourist Destinations\",\n    \"advancements.nether.explore_nether.description\": \"Explore all Nether biomes\",\n    \"advancements.nether.find_bastion.title\": \"Those Were the Days\",\n    \"advancements.nether.find_bastion.description\": \"Enter a Bastion Remnant\",\n    \"advancements.nether.loot_bastion.title\": \"War Pigs\",\n    \"advancements.nether.loot_bastion.description\": \"Loot a chest in a Bastion Remnant\",\n    \"advancements.nether.distract_piglin.title\": \"Oh Shiny\",\n    \"advancements.nether.distract_piglin.description\": \"Distract Piglins with gold\",\n    \"argument.uuid.invalid\": \"Invalid UUID\",\n    \"commands.attribute.failed.entity\": \"%s is not a valid entity for this command\",\n    \"commands.attribute.failed.no_attribute\": \"Entity %s has no attribute %s\",\n    \"commands.attribute.failed.no_modifier\": \"Attribute %s for entity %s has no modifier %s\",\n    \"commands.attribute.failed.modifier_already_present\": \"Modifier %s is already present on attribute %s for entity %s\",\n    \"commands.attribute.value.get.success\": \"Value of attribute %s for entity %s is %s\",\n    \"commands.attribute.base_value.get.success\": \"Base value of attribute %s for entity %s is %s\",\n    \"commands.attribute.base_value.set.success\": \"Base value for attribute %s for entity %s set to %s\",\n    \"commands.attribute.modifier.add.success\": \"Added modifier %s to attribute %s for entity %s\",\n    \"commands.attribute.modifier.remove.success\": \"Removed modifier %s from attribute %s for entity %s\",\n    \"commands.attribute.modifier.value.get.success\": \"Value of modifier %s on attribute %s for entity %s is %s\",\n    \"commands.locatebiome.success\": \"The nearest %s is at %s (%s blocks away)\",\n    \"commands.teleport.invalidPosition\": \"Invalid position for teleport\",\n    \"commands.reload.failure\": \"Reload failed, keeping old data\",\n    \"commands.datapack.modify.enable\": \"Enabling data pack %s\",\n    \"commands.datapack.modify.disable\": \"Disabling data pack %s\",\n    \"commands.locatebiome.notFound\": \"Could not find a biome of type \\\"%s\\\" within reasonable distance\",\n    \"commands.locatebiome.invalid\": \"There is no biome with type \\\"%s\\\"\",\n    \"commands.summon.invalidPosition\": \"Invalid position for summon\",\n    \"biome.minecraft.nether_wastes\": \"Nether Wastes\",\n    \"biome.minecraft.soul_sand_valley\": \"Soul Sand Valley\",\n    \"biome.minecraft.warped_forest\": \"Warped Forest\",\n    \"biome.minecraft.crimson_forest\": \"Crimson Forest\",\n    \"biome.minecraft.basalt_deltas\": \"Basalt Deltas\",\n    \"gamerule.announceAdvancements\": \"Announce advancements\",\n    \"gamerule.commandBlockOutput\": \"Broadcast command block output\",\n    \"gamerule.disableElytraMovementCheck\": \"Disable elytra movement check\",\n    \"gamerule.disableRaids\": \"Disable raids\",\n    \"gamerule.doDaylightCycle\": \"Advance in-game time\",\n    \"gamerule.doEntityDrops\": \"Drop entity equipment\",\n    \"gamerule.doEntityDrops.description\": \"Controls drops from minecarts (including inventories), item frames, boats, etc.\",\n    \"gamerule.doFireTick\": \"Update fire\",\n    \"gamerule.doImmediateRespawn\": \"Respawn immediately\",\n    \"gamerule.doInsomnia\": \"Spawn phantoms\",\n    \"gamerule.doLimitedCrafting\": \"Require recipe for crafting\",\n    \"gamerule.doLimitedCrafting.description\": \"If enabled, players will be able to craft only unlocked recipes\",\n    \"gamerule.doMobLoot\": \"Drop mob loot\",\n    \"gamerule.doMobLoot.description\": \"Controls resource drops from mobs, including experience orbs\",\n    \"gamerule.doMobSpawning\": \"Spawn mobs\",\n    \"gamerule.doMobSpawning.description\": \"Some entities might have separate rules\",\n    \"gamerule.doPatrolSpawning\": \"Spawn pillager patrols\",\n    \"gamerule.doTileDrops\": \"Drop blocks\",\n    \"gamerule.doTileDrops.description\": \"Controls resource drops from blocks, including experience orbs\",\n    \"gamerule.doTraderSpawning\": \"Spawn wandering traders\",\n    \"gamerule.doWeatherCycle\": \"Update weather\",\n    \"gamerule.drowningDamage\": \"Deal drowning damage\",\n    \"gamerule.fallDamage\": \"Deal fall damage\",\n    \"gamerule.fireDamage\": \"Deal fire damage\",\n    \"gamerule.forgiveDeadPlayers\": \"Forgive dead players\",\n    \"gamerule.forgiveDeadPlayers.description\": \"Angered neutral mobs stop being angry when the targeted player dies nearby.\",\n    \"gamerule.keepInventory\": \"Keep inventory after death\",\n    \"gamerule.logAdminCommands\": \"Broadcast admin commands\",\n    \"gamerule.maxCommandChainLength\": \"Command chain size limit\",\n    \"gamerule.maxCommandChainLength.description\": \"Applies to command block chains and functions\",\n    \"gamerule.maxEntityCramming\": \"Entity cramming threshold\",\n    \"gamerule.mobGriefing\": \"Allow destructive mob actions\",\n    \"gamerule.naturalRegeneration\": \"Regenerate health\",\n    \"gamerule.randomTickSpeed\": \"Random tick speed rate\",\n    \"gamerule.reducedDebugInfo\": \"Reduce debug info\",\n    \"gamerule.reducedDebugInfo.description\": \"Limits contents of debug screen\",\n    \"gamerule.sendCommandFeedback\": \"Send command feedback\",\n    \"gamerule.showDeathMessages\": \"Show death messages\",\n    \"gamerule.spawnRadius\": \"Respawn location radius\",\n    \"gamerule.spectatorsGenerateChunks\": \"Allow spectators to generate terrain\",\n    \"gamerule.universalAnger\": \"Universal anger\",\n    \"gamerule.universalAnger.description\": \"Angered neutral mobs attack any nearby player, not just the player that angered them. Works best if forgiveDeadPlayers is disabled.\",\n    \"gamerule.category.chat\": \"Chat\",\n    \"gamerule.category.spawning\": \"Spawning\",\n    \"gamerule.category.updates\": \"World updates\",\n    \"gamerule.category.drops\": \"Drops\",\n    \"gamerule.category.mobs\": \"Mobs\",\n    \"gamerule.category.player\": \"Player\",\n    \"gamerule.category.misc\": \"Miscellaneous\",\n    \"pack.source.builtin\": \"built-in\",\n    \"pack.source.world\": \"world\",\n    \"pack.source.local\": \"local\",\n    \"pack.source.server\": \"server\"\n  },\n  \"1.15\": {\n    \"narration.suggestion.tooltip\": \"Selected suggestion %d out of %d: %s (%s)\",\n    \"narration.suggestion\": \"Selected suggestion %d out of %d: %s\",\n    \"chat.copy.click\": \"Click to copy to Clipboard\",\n    \"options.biomeBlendRadius.1\": \"OFF (Fastest)\",\n    \"options.biomeBlendRadius.3\": \"3x3 (Fast)\",\n    \"options.biomeBlendRadius.5\": \"5x5 (Normal)\",\n    \"options.biomeBlendRadius.7\": \"7x7 (High)\",\n    \"options.biomeBlendRadius.9\": \"9x9 (Very High)\",\n    \"options.biomeBlendRadius.11\": \"11x11 (Extreme)\",\n    \"options.biomeBlendRadius.13\": \"13x13 (Showoff)\",\n    \"options.biomeBlendRadius.15\": \"15x15 (Maximum)\",\n    \"options.key.toggle\": \"Toggle\",\n    \"options.key.hold\": \"Hold\",\n    \"resourcePack.load_fail\": \"Resource reload failed\",\n    \"block.minecraft.bed.set_spawn\": \"Respawn point set\",\n    \"block.minecraft.beehive\": \"Beehive\",\n    \"block.minecraft.bee_nest\": \"Bee Nest\",\n    \"block.minecraft.honey_block\": \"Honey Block\",\n    \"block.minecraft.honeycomb_block\": \"Honeycomb Block\",\n    \"item.minecraft.bee_spawn_egg\": \"Bee Spawn Egg\",\n    \"item.minecraft.honey_bottle\": \"Honey Bottle\",\n    \"item.minecraft.honeycomb\": \"Honeycomb\",\n    \"entity.minecraft.bee\": \"Bee\",\n    \"death.attack.sting\": \"%1$s was stung to death\",\n    \"death.attack.sting.player\": \"%1$s was stung to death by %2$s\",\n    \"stat.minecraft.interact_with_anvil\": \"Interactions with Anvil\",\n    \"stat.minecraft.interact_with_grindstone\": \"Interactions with Grindstone\",\n    \"subtitles.block.beehive.enter\": \"Bee enters hive\",\n    \"subtitles.block.beehive.exit\": \"Bee leaves hive\",\n    \"subtitles.block.beehive.drip\": \"Honey drips\",\n    \"subtitles.block.beehive.work\": \"Bees work\",\n    \"subtitles.block.beehive.shear\": \"Shears scrape\",\n    \"subtitles.block.honey_block.slide\": \"Sliding down a honey block\",\n    \"subtitles.entity.bee.ambient\": \"Bee buzzes\",\n    \"subtitles.entity.bee.death\": \"Bee dies\",\n    \"subtitles.entity.bee.hurt\": \"Bee hurts\",\n    \"subtitles.entity.bee.loop\": \"Bee buzzes\",\n    \"subtitles.entity.bee.loop_aggressive\": \"Bee buzzes angrily\",\n    \"subtitles.entity.bee.pollinate\": \"Bee buzzes happily\",\n    \"subtitles.entity.bee.sting\": \"Bee stings\",\n    \"subtitles.entity.iron_golem.damage\": \"Iron Golem breaks\",\n    \"subtitles.entity.iron_golem.repair\": \"Iron Golem repaired\",\n    \"subtitles.item.honey_bottle.drink\": \"Honey gulping\",\n    \"advancements.adventure.honey_block_slide.title\": \"Sticky Situation\",\n    \"advancements.adventure.honey_block_slide.description\": \"Jump into a Honey Block to break your fall\",\n    \"advancements.husbandry.safely_harvest_honey.title\": \"Bee Our Guest\",\n    \"advancements.husbandry.safely_harvest_honey.description\": \"Use a Campfire to collect Honey from a Beehive using a Bottle without aggravating the bees\",\n    \"advancements.husbandry.silk_touch_nest.title\": \"Total Beelocation\",\n    \"advancements.husbandry.silk_touch_nest.description\": \"Move a Bee Nest, with 3 bees inside, using Silk Touch\",\n    \"argument.entity.options.predicate.description\": \"Custom predicate\",\n    \"commands.schedule.cleared.success\": \"Removed %s schedules with id %s\",\n    \"commands.schedule.cleared.failure\": \"No schedules with id %s\",\n    \"commands.data.storage.modified\": \"Modified storage %s\",\n    \"commands.data.storage.query\": \"Storage %s has the following contents: %s\",\n    \"commands.data.storage.get\": \"%s in storage %s after scale factor of %s is %s\",\n    \"commands.spectate.success.stopped\": \"No longer spectating an entity\",\n    \"commands.spectate.success.started\": \"Now spectating %s\",\n    \"commands.spectate.not_spectator\": \"%s is not in spectator mode\",\n    \"commands.spectate.self\": \"Cannot spectate yourself\",\n    \"predicate.unknown\": \"Unknown predicate: %s\"\n  },\n  \"1.14\": {\n    \"narrator.button.accessibility\": \"Accessibility\",\n    \"narrator.button.language\": \"Language\",\n    \"narrator.button.difficulty_lock\": \"Difficulty lock\",\n    \"narrator.button.difficulty_lock.unlocked\": \"Unlocked\",\n    \"narrator.button.difficulty_lock.locked\": \"Locked\",\n    \"narrator.screen.title\": \"Title Screen\",\n    \"narrator.controls.reset\": \"Reset %s button\",\n    \"narrator.controls.bound\": \"%s is bound to %s\",\n    \"narrator.controls.unbound\": \"%s is not bound\",\n    \"narrator.select\": \"Selected: %s\",\n    \"narrator.select.world\": \"Selected %s, last played: %s, %s, %s, version: %s\",\n    \"narrator.loading\": \"Loading: %s\",\n    \"narrator.loading.done\": \"Done\",\n    \"narrator.joining\": \"Joining\",\n    \"gui.recipebook.toggleRecipes.blastable\": \"Showing blastable\",\n    \"gui.recipebook.toggleRecipes.smokable\": \"Showing smokable\",\n    \"gui.narrate.button\": \"%s button\",\n    \"gui.narrate.slider\": \"%s slider\",\n    \"gui.narrate.editBox\": \"%s edit box: %s\",\n    \"menu.sendFeedback\": \"Give Feedback\",\n    \"menu.reportBugs\": \"Report Bugs\",\n    \"menu.paused\": \"Game paused\",\n    \"selectWorld.search\": \"search for worlds\",\n    \"selectWorld.edit.backupFailed\": \"Backup failed\",\n    \"selectWorld.backupEraseCache\": \"Erase cached data\",\n    \"chat.editBox\": \"chat\",\n    \"chat.type.team.text\": \"%s <%s> %s\",\n    \"chat.type.team.sent\": \"-> %s <%s> %s\",\n    \"chat.type.team.hover\": \"Message Team\",\n    \"options.mouse_settings\": \"Mouse Settings...\",\n    \"options.mouse_settings.title\": \"Mouse Settings\",\n    \"options.accessibility.title\": \"Accessibility Settings...\",\n    \"options.accessibility.text_background\": \"Text Background\",\n    \"options.accessibility.text_background.chat\": \"Chat\",\n    \"options.accessibility.text_background.everywhere\": \"Everywhere\",\n    \"options.accessibility.text_background_opacity\": \"Text Background Opacity\",\n    \"options.discrete_mouse_scroll\": \"Discrete Scrolling\",\n    \"options.mouseWheelSensitivity\": \"Scroll Sensitivity\",\n    \"options.rawMouseInput\": \"Raw input\",\n    \"options.fullscreen.unavailable\": \"Setting unavailable\",\n    \"title.oldgl.eol.line1\": \"Old graphics card detected; this WILL prevent you from\",\n    \"title.oldgl.eol.line2\": \"playing future updates as OpenGL 2.0 will be required!\",\n    \"title.oldgl.deprecation.line1\": \"Old graphics card detected; this may prevent you from\",\n    \"title.oldgl.deprecation.line2\": \"playing in the future as OpenGL 3.2 will be required!\",\n    \"merchant.current_level\": \"Trader's current level\",\n    \"merchant.next_level\": \"Trader's next level\",\n    \"merchant.level.1\": \"Novice\",\n    \"merchant.level.2\": \"Apprentice\",\n    \"merchant.level.3\": \"Journeyman\",\n    \"merchant.level.4\": \"Expert\",\n    \"merchant.level.5\": \"Master\",\n    \"merchant.trades\": \"Trades\",\n    \"block.minecraft.cornflower\": \"Cornflower\",\n    \"block.minecraft.lily_of_the_valley\": \"Lily of the Valley\",\n    \"block.minecraft.wither_rose\": \"Wither Rose\",\n    \"block.minecraft.smooth_stone_slab\": \"Smooth Stone Slab\",\n    \"block.minecraft.cut_sandstone_slab\": \"Cut Sandstone Slab\",\n    \"block.minecraft.cut_red_sandstone_slab\": \"Cut Red Sandstone Slab\",\n    \"block.minecraft.oak_sign\": \"Oak Sign\",\n    \"block.minecraft.spruce_sign\": \"Spruce Sign\",\n    \"block.minecraft.birch_sign\": \"Birch Sign\",\n    \"block.minecraft.acacia_sign\": \"Acacia Sign\",\n    \"block.minecraft.jungle_sign\": \"Jungle Sign\",\n    \"block.minecraft.dark_oak_sign\": \"Dark Oak Sign\",\n    \"block.minecraft.oak_wall_sign\": \"Oak Wall Sign\",\n    \"block.minecraft.spruce_wall_sign\": \"Spruce Wall Sign\",\n    \"block.minecraft.birch_wall_sign\": \"Birch Wall Sign\",\n    \"block.minecraft.acacia_wall_sign\": \"Acacia Wall Sign\",\n    \"block.minecraft.jungle_wall_sign\": \"Jungle Wall Sign\",\n    \"block.minecraft.dark_oak_wall_sign\": \"Dark Oak Wall Sign\",\n    \"block.minecraft.scaffolding\": \"Scaffolding\",\n    \"block.minecraft.bed.obstructed\": \"This bed is obstructed\",\n    \"block.minecraft.potted_cornflower\": \"Potted Cornflower\",\n    \"block.minecraft.potted_lily_of_the_valley\": \"Potted Lily of the Valley\",\n    \"block.minecraft.potted_wither_rose\": \"Potted Wither Rose\",\n    \"block.minecraft.potted_bamboo\": \"Potted Bamboo\",\n    \"block.minecraft.loom\": \"Loom\",\n    \"block.minecraft.bamboo\": \"Bamboo\",\n    \"block.minecraft.bamboo_sapling\": \"Bamboo Sapling\",\n    \"block.minecraft.jigsaw\": \"Jigsaw Block\",\n    \"block.minecraft.composter\": \"Composter\",\n    \"block.minecraft.polished_granite_stairs\": \"Polished Granite Stairs\",\n    \"block.minecraft.smooth_red_sandstone_stairs\": \"Smooth Red Sandstone Stairs\",\n    \"block.minecraft.mossy_stone_brick_stairs\": \"Mossy Stone Brick Stairs\",\n    \"block.minecraft.polished_diorite_stairs\": \"Polished Diorite Stairs\",\n    \"block.minecraft.mossy_cobblestone_stairs\": \"Mossy Cobblestone Stairs\",\n    \"block.minecraft.end_stone_brick_stairs\": \"End Stone Brick Stairs\",\n    \"block.minecraft.stone_stairs\": \"Stone Stairs\",\n    \"block.minecraft.smooth_sandstone_stairs\": \"Smooth Sandstone Stairs\",\n    \"block.minecraft.smooth_quartz_stairs\": \"Smooth Quartz Stairs\",\n    \"block.minecraft.granite_stairs\": \"Granite Stairs\",\n    \"block.minecraft.andesite_stairs\": \"Andesite Stairs\",\n    \"block.minecraft.red_nether_brick_stairs\": \"Red Nether Brick Stairs\",\n    \"block.minecraft.polished_andesite_stairs\": \"Polished Andesite Stairs\",\n    \"block.minecraft.diorite_stairs\": \"Diorite Stairs\",\n    \"block.minecraft.polished_granite_slab\": \"Polished Granite Slab\",\n    \"block.minecraft.smooth_red_sandstone_slab\": \"Smooth Red Sandstone Slab\",\n    \"block.minecraft.mossy_stone_brick_slab\": \"Mossy Stone Brick Slab\",\n    \"block.minecraft.polished_diorite_slab\": \"Polished Diorite Slab\",\n    \"block.minecraft.mossy_cobblestone_slab\": \"Mossy Cobblestone Slab\",\n    \"block.minecraft.end_stone_brick_slab\": \"End Stone Brick Slab\",\n    \"block.minecraft.smooth_sandstone_slab\": \"Smooth Sandstone Slab\",\n    \"block.minecraft.smooth_quartz_slab\": \"Smooth Quartz Slab\",\n    \"block.minecraft.granite_slab\": \"Granite Slab\",\n    \"block.minecraft.andesite_slab\": \"Andesite Slab\",\n    \"block.minecraft.red_nether_brick_slab\": \"Red Nether Brick Slab\",\n    \"block.minecraft.polished_andesite_slab\": \"Polished Andesite Slab\",\n    \"block.minecraft.diorite_slab\": \"Diorite Slab\",\n    \"block.minecraft.brick_wall\": \"Brick Wall\",\n    \"block.minecraft.prismarine_wall\": \"Prismarine Wall\",\n    \"block.minecraft.red_sandstone_wall\": \"Red Sandstone Wall\",\n    \"block.minecraft.mossy_stone_brick_wall\": \"Mossy Stone Brick Wall\",\n    \"block.minecraft.granite_wall\": \"Granite Wall\",\n    \"block.minecraft.stone_brick_wall\": \"Stone Brick Wall\",\n    \"block.minecraft.nether_brick_wall\": \"Nether Brick Wall\",\n    \"block.minecraft.andesite_wall\": \"Andesite Wall\",\n    \"block.minecraft.red_nether_brick_wall\": \"Red Nether Brick Wall\",\n    \"block.minecraft.sandstone_wall\": \"Sandstone Wall\",\n    \"block.minecraft.end_stone_brick_wall\": \"End Stone Brick Wall\",\n    \"block.minecraft.diorite_wall\": \"Diorite Wall\",\n    \"block.minecraft.barrel\": \"Barrel\",\n    \"block.minecraft.smoker\": \"Smoker\",\n    \"block.minecraft.blast_furnace\": \"Blast Furnace\",\n    \"block.minecraft.cartography_table\": \"Cartography Table\",\n    \"block.minecraft.fletching_table\": \"Fletching Table\",\n    \"block.minecraft.smithing_table\": \"Smithing Table\",\n    \"block.minecraft.grindstone\": \"Grindstone\",\n    \"block.minecraft.lectern\": \"Lectern\",\n    \"block.minecraft.stonecutter\": \"Stonecutter\",\n    \"block.minecraft.bell\": \"Bell\",\n    \"block.minecraft.ominous_banner\": \"Ominous Banner\",\n    \"block.minecraft.lantern\": \"Lantern\",\n    \"block.minecraft.sweet_berry_bush\": \"Sweet Berry Bush\",\n    \"block.minecraft.campfire\": \"Campfire\",\n    \"item.minecraft.red_dye\": \"Red Dye\",\n    \"item.minecraft.green_dye\": \"Green Dye\",\n    \"item.minecraft.yellow_dye\": \"Yellow Dye\",\n    \"item.minecraft.blue_dye\": \"Blue Dye\",\n    \"item.minecraft.black_dye\": \"Black Dye\",\n    \"item.minecraft.brown_dye\": \"Brown Dye\",\n    \"item.minecraft.white_dye\": \"White Dye\",\n    \"item.minecraft.cat_spawn_egg\": \"Cat Spawn Egg\",\n    \"item.minecraft.ravager_spawn_egg\": \"Ravager Spawn Egg\",\n    \"item.minecraft.panda_spawn_egg\": \"Panda Spawn Egg\",\n    \"item.minecraft.pillager_spawn_egg\": \"Pillager Spawn Egg\",\n    \"item.minecraft.fox_spawn_egg\": \"Fox Spawn Egg\",\n    \"item.minecraft.trader_llama_spawn_egg\": \"Trader Llama Spawn Egg\",\n    \"item.minecraft.wandering_trader_spawn_egg\": \"Wandering Trader Spawn Egg\",\n    \"item.minecraft.leather_horse_armor\": \"Leather Horse Armor\",\n    \"item.minecraft.crossbow\": \"Crossbow\",\n    \"item.minecraft.crossbow.projectile\": \"Projectile:\",\n    \"item.minecraft.suspicious_stew\": \"Suspicious Stew\",\n    \"item.minecraft.creeper_banner_pattern\": \"Banner Pattern\",\n    \"item.minecraft.skull_banner_pattern\": \"Banner Pattern\",\n    \"item.minecraft.flower_banner_pattern\": \"Banner Pattern\",\n    \"item.minecraft.mojang_banner_pattern\": \"Banner Pattern\",\n    \"item.minecraft.globe_banner_pattern\": \"Banner Pattern\",\n    \"item.minecraft.creeper_banner_pattern.desc\": \"Creeper Charge\",\n    \"item.minecraft.skull_banner_pattern.desc\": \"Skull Charge\",\n    \"item.minecraft.flower_banner_pattern.desc\": \"Flower Charge\",\n    \"item.minecraft.mojang_banner_pattern.desc\": \"Thing\",\n    \"item.minecraft.globe_banner_pattern.desc\": \"Globe\",\n    \"item.minecraft.sweet_berries\": \"Sweet Berries\",\n    \"container.smoker\": \"Smoker\",\n    \"container.lectern\": \"Lectern\",\n    \"container.blast_furnace\": \"Blast Furnace\",\n    \"container.barrel\": \"Barrel\",\n    \"container.loom\": \"Loom\",\n    \"container.grindstone_title\": \"Repair & Disenchant\",\n    \"container.cartography_table\": \"Cartography Table\",\n    \"container.stonecutter\": \"Stonecutter\",\n    \"structure_block.position.x\": \"relative Position x\",\n    \"structure_block.position.y\": \"relative position y\",\n    \"structure_block.position.z\": \"relative position z\",\n    \"structure_block.size.x\": \"structure size x\",\n    \"structure_block.size.y\": \"structure size y\",\n    \"structure_block.size.z\": \"structure size z\",\n    \"structure_block.integrity.integrity\": \"Structure Integrity\",\n    \"structure_block.integrity.seed\": \"Structure Seed\",\n    \"jigsaw_block.target_pool\": \"Target pool:\",\n    \"jigsaw_block.attachement_type\": \"Attachment type:\",\n    \"jigsaw_block.final_state\": \"Turns into:\",\n    \"filled_map.locked\": \"Locked\",\n    \"entity.minecraft.fox\": \"Fox\",\n    \"entity.minecraft.ravager\": \"Ravager\",\n    \"entity.minecraft.panda\": \"Panda\",\n    \"entity.minecraft.pillager\": \"Pillager\",\n    \"entity.minecraft.trader_llama\": \"Trader Llama\",\n    \"entity.minecraft.villager.mason\": \"Mason\",\n    \"entity.minecraft.villager.none\": \"Villager\",\n    \"entity.minecraft.villager.toolsmith\": \"Toolsmith\",\n    \"entity.minecraft.villager.weaponsmith\": \"Weaponsmith\",\n    \"entity.minecraft.wandering_trader\": \"Wandering Trader\",\n    \"death.attack.sweetBerryBush\": \"%1$s was poked to death by a sweet berry bush\",\n    \"death.attack.sweetBerryBush.player\": \"%1$s was poked to death by a sweet berry bush whilst trying to escape %2$s\",\n    \"effect.minecraft.bad_omen\": \"Bad Omen\",\n    \"effect.minecraft.hero_of_the_village\": \"Hero of the Village\",\n    \"event.minecraft.raid\": \"Raid\",\n    \"event.minecraft.raid.raiders_remaining\": \"Raiders remaining: %s\",\n    \"event.minecraft.raid.victory\": \"Victory\",\n    \"event.minecraft.raid.defeat\": \"Defeat\",\n    \"enchantment.minecraft.multishot\": \"Multishot\",\n    \"enchantment.minecraft.quick_charge\": \"Quick Charge\",\n    \"enchantment.minecraft.piercing\": \"Piercing\",\n    \"stat.minecraft.bell_ring\": \"Bells Rung\",\n    \"stat.minecraft.interact_with_campfire\": \"Interactions with Campfire\",\n    \"stat.minecraft.interact_with_cartography_table\": \"Interactions with Cartography Table\",\n    \"stat.minecraft.interact_with_lectern\": \"Interactions with Lectern\",\n    \"stat.minecraft.interact_with_loom\": \"Interactions with Loom\",\n    \"stat.minecraft.interact_with_blast_furnace\": \"Interactions with Blast Furnace\",\n    \"stat.minecraft.interact_with_smoker\": \"Interactions with Smoker\",\n    \"stat.minecraft.interact_with_stonecutter\": \"Interactions with Stonecutter\",\n    \"stat.minecraft.open_barrel\": \"Barrels Opened\",\n    \"stat.minecraft.raid_trigger\": \"Raids Triggered\",\n    \"stat.minecraft.raid_win\": \"Raids Won\",\n    \"stat.minecraft.ring_bell\": \"Bells Rung\",\n    \"block.minecraft.banner.globe.black\": \"Black Globe\",\n    \"block.minecraft.banner.globe.red\": \"Red Globe\",\n    \"block.minecraft.banner.globe.green\": \"Green Globe\",\n    \"block.minecraft.banner.globe.brown\": \"Brown Globe\",\n    \"block.minecraft.banner.globe.blue\": \"Blue Globe\",\n    \"block.minecraft.banner.globe.purple\": \"Purple Globe\",\n    \"block.minecraft.banner.globe.cyan\": \"Cyan Globe\",\n    \"block.minecraft.banner.globe.light_gray\": \"Light Gray Globe\",\n    \"block.minecraft.banner.globe.gray\": \"Gray Globe\",\n    \"block.minecraft.banner.globe.pink\": \"Pink Globe\",\n    \"block.minecraft.banner.globe.lime\": \"Lime Globe\",\n    \"block.minecraft.banner.globe.yellow\": \"Yellow Globe\",\n    \"block.minecraft.banner.globe.light_blue\": \"Light Blue Globe\",\n    \"block.minecraft.banner.globe.magenta\": \"Magenta Globe\",\n    \"block.minecraft.banner.globe.orange\": \"Orange Globe\",\n    \"block.minecraft.banner.globe.white\": \"White Globe\",\n    \"subtitles.block.barrel.close\": \"Barrel closes\",\n    \"subtitles.block.barrel.open\": \"Barrel opens\",\n    \"subtitles.block.bell.use\": \"Bell rings\",\n    \"subtitles.block.bell.resonate\": \"Bell resonates\",\n    \"subtitles.block.blastfurnace.fire_crackle\": \"Blast furnace crackles\",\n    \"subtitles.block.campfire.crackle\": \"Campfire crackles\",\n    \"subtitles.block.grindstone.use\": \"Grindstone used\",\n    \"subtitles.block.smoker.smoke\": \"Smoker smokes\",\n    \"subtitles.entity.parrot.imitate.guardian\": \"Parrot moans\",\n    \"subtitles.entity.parrot.imitate.panda\": \"Parrot pants\",\n    \"subtitles.entity.parrot.imitate.pillager\": \"Parrot murmurs\",\n    \"subtitles.entity.parrot.imitate.ravager\": \"Parrot grunts\",\n    \"subtitles.entity.evoker.celebrate\": \"Evoker cheers\",\n    \"subtitles.entity.fox.aggro\": \"Fox angers\",\n    \"subtitles.entity.fox.ambient\": \"Fox squeaks\",\n    \"subtitles.entity.fox.bite\": \"Fox bites\",\n    \"subtitles.entity.fox.death\": \"Fox dies\",\n    \"subtitles.entity.fox.eat\": \"Fox eats\",\n    \"subtitles.entity.fox.hurt\": \"Fox hurts\",\n    \"subtitles.entity.fox.screech\": \"Fox screeches\",\n    \"subtitles.entity.fox.sleep\": \"Fox snores\",\n    \"subtitles.entity.fox.sniff\": \"Fox sniffs\",\n    \"subtitles.entity.fox.spit\": \"Fox spits\",\n    \"subtitles.entity.ravager.step\": \"Ravager steps\",\n    \"subtitles.entity.ravager.stunned\": \"Ravager stunned\",\n    \"subtitles.entity.ravager.roar\": \"Ravager roars\",\n    \"subtitles.entity.ravager.attack\": \"Ravager bites\",\n    \"subtitles.entity.ravager.death\": \"Ravager dies\",\n    \"subtitles.entity.ravager.hurt\": \"Ravager hurts\",\n    \"subtitles.entity.ravager.ambient\": \"Ravager grunts\",\n    \"subtitles.entity.ravager.celebrate\": \"Ravager cheers\",\n    \"subtitles.entity.mooshroom.convert\": \"Mooshroom transforms\",\n    \"subtitles.entity.mooshroom.eat\": \"Mooshroom eats\",\n    \"subtitles.entity.mooshroom.milk\": \"Mooshroom gets milked\",\n    \"subtitles.entity.mooshroom.suspicious_milk\": \"Mooshroom gets milked suspiciously\",\n    \"subtitles.entity.panda.ambient\": \"Panda pants\",\n    \"subtitles.entity.panda.pre_sneeze\": \"Panda's nose tickles\",\n    \"subtitles.entity.panda.sneeze\": \"Panda sneezes\",\n    \"subtitles.entity.panda.death\": \"Panda dies\",\n    \"subtitles.entity.panda.eat\": \"Panda eats\",\n    \"subtitles.entity.panda.step\": \"Panda steps\",\n    \"subtitles.entity.panda.cant_breed\": \"Panda bleats\",\n    \"subtitles.entity.panda.aggressive_ambient\": \"Panda huffs\",\n    \"subtitles.entity.panda.worried_ambient\": \"Panda whimpers\",\n    \"subtitles.entity.panda.hurt\": \"Panda hurts\",\n    \"subtitles.entity.panda.bite\": \"Panda bites\",\n    \"subtitles.entity.pillager.ambient\": \"Pillager murmurs\",\n    \"subtitles.entity.pillager.celebrate\": \"Pillager cheers\",\n    \"subtitles.entity.pillager.death\": \"Pillager dies\",\n    \"subtitles.entity.pillager.hurt\": \"Pillager hurts\",\n    \"subtitles.entity.villager.celebrate\": \"Villager cheers\",\n    \"subtitles.entity.villager.work_armorer\": \"Armorer works\",\n    \"subtitles.entity.villager.work_butcher\": \"Butcher works\",\n    \"subtitles.entity.villager.work_cartographer\": \"Cartographer works\",\n    \"subtitles.entity.villager.work_cleric\": \"Cleric works\",\n    \"subtitles.entity.villager.work_farmer\": \"Farmer works\",\n    \"subtitles.entity.villager.work_fisherman\": \"Fisherman works\",\n    \"subtitles.entity.villager.work_fletcher\": \"Fletcher works\",\n    \"subtitles.entity.villager.work_leatherworker\": \"Leatherworker works\",\n    \"subtitles.entity.villager.work_librarian\": \"Librarian works\",\n    \"subtitles.entity.villager.work_mason\": \"Mason works\",\n    \"subtitles.entity.villager.work_shepherd\": \"Shepherd works\",\n    \"subtitles.entity.villager.work_toolsmith\": \"Toolsmith works\",\n    \"subtitles.entity.villager.work_weaponsmith\": \"Weaponsmith works\",\n    \"subtitles.entity.vindicator.celebrate\": \"Vindicator cheers\",\n    \"subtitles.entity.wandering_trader.ambient\": \"Wandering Trader mumbles\",\n    \"subtitles.entity.wandering_trader.death\": \"Wandering Trader dies\",\n    \"subtitles.entity.wandering_trader.hurt\": \"Wandering Trader hurts\",\n    \"subtitles.entity.wandering_trader.no\": \"Wandering Trader disagrees\",\n    \"subtitles.entity.wandering_trader.trade\": \"Wandering Trader trades\",\n    \"subtitles.entity.wandering_trader.yes\": \"Wandering Trader agrees\",\n    \"subtitles.entity.witch.celebrate\": \"Witch cheers\",\n    \"subtitles.event.raid.horn\": \"Ominous horn blares\",\n    \"subtitles.item.crop.plant\": \"Crop planted\",\n    \"subtitles.item.crossbow.charge\": \"Crossbow charges up\",\n    \"subtitles.item.crossbow.hit\": \"Arrow hits\",\n    \"subtitles.item.crossbow.load\": \"Crossbow loads\",\n    \"subtitles.item.crossbow.shoot\": \"Crossbow fires\",\n    \"subtitles.item.nether_wart.plant\": \"Crop planted\",\n    \"subtitles.item.berries.pick\": \"Berries pop\",\n    \"subtitles.item.book.page_turn\": \"Page rustles\",\n    \"subtitles.item.book.put\": \"Book thumps\",\n    \"debug.pause.help\": \"F3 + Esc = Pause without pause menu (if pausing is possible)\",\n    \"advancements.adventure.arbalistic.title\": \"Arbalistic\",\n    \"advancements.adventure.arbalistic.description\": \"Kill five unique mobs with one crossbow shot\",\n    \"advancements.adventure.hero_of_the_village.title\": \"Hero of the Village\",\n    \"advancements.adventure.hero_of_the_village.description\": \"Successfully defend a village from a raid\",\n    \"advancements.adventure.ol_betsy.title\": \"Ol' Betsy\",\n    \"advancements.adventure.ol_betsy.description\": \"Shoot a crossbow\",\n    \"advancements.adventure.two_birds_one_arrow.title\": \"Two Birds, One Arrow\",\n    \"advancements.adventure.two_birds_one_arrow.description\": \"Kill two Phantoms with a piercing arrow\",\n    \"advancements.adventure.voluntary_exile.title\": \"Voluntary Exile\",\n    \"advancements.adventure.voluntary_exile.description\": \"Kill a raid captain.\\nMaybe consider staying away from villages for the time being...\",\n    \"advancements.adventure.whos_the_pillager_now.title\": \"Who's the Pillager Now?\",\n    \"advancements.adventure.whos_the_pillager_now.description\": \"Give a Pillager a taste of their own medicine\",\n    \"advancements.husbandry.complete_catalogue.title\": \"A Complete Catalogue\",\n    \"advancements.husbandry.complete_catalogue.description\": \"Tame all cat variants!\",\n    \"commands.debug.reportSaved\": \"Created debug report in %s\",\n    \"commands.debug.reportFailed\": \"Failed to create debug report\",\n    \"commands.drop.no_held_items\": \"Entity can't hold any items\",\n    \"commands.drop.no_loot_table\": \"Entity %s has no loot table\",\n    \"commands.drop.success.single\": \"Dropped %s * %s\",\n    \"commands.drop.success.single_with_table\": \"Dropped %s * %s from loot table %s\",\n    \"commands.drop.success.multiple\": \"Dropped %s items\",\n    \"commands.drop.success.multiple_with_table\": \"Dropped %s items from loot table %s\",\n    \"commands.schedule.created.function\": \"Scheduled function '%s' in %s ticks at gametime %s\",\n    \"commands.schedule.created.tag\": \"Scheduled tag '%s' in %s ticks at gametime %s\",\n    \"commands.schedule.same_tick\": \"Can't schedule for current tick\",\n    \"arguments.nbtpath.nothing_found\": \"Found no elements matching %s\",\n    \"argument.time.invalid_unit\": \"Invalid unit\",\n    \"argument.time.invalid_tick_count\": \"Tick count must be non-negative\",\n    \"commands.data.modify.expected_list\": \"Expected list, got: %s\",\n    \"commands.data.modify.expected_object\": \"Expected object, got: %s\",\n    \"commands.data.modify.invalid_index\": \"Invalid list index: %s\",\n    \"commands.data.get.multiple\": \"This argument accepts a single NBT value\",\n    \"commands.teammsg.failed.noteam\": \"You must be on a team to message your team\",\n    \"lectern.take_book\": \"Take Book\",\n    \"argument.long.low\": \"Long must not be less than %s, found %s\",\n    \"argument.long.big\": \"Long must not be more than %s, found %s\",\n    \"parsing.long.invalid\": \"Invalid long '%s'\",\n    \"parsing.long.expected\": \"Expected long\",\n    \"biome.minecraft.bamboo_jungle\": \"Bamboo Jungle\",\n    \"biome.minecraft.bamboo_jungle_hills\": \"Bamboo Jungle Hills\"\n  },\n  \"1.13.1\": {\n    \"menu.loadingForcedChunks\": \"Loading forced chunks for dimension %s\",\n    \"optimizeWorld.stage.structures\": \"Upgrading structure data...\",\n    \"resourcePack.broken_assets\": \"BROKEN ASSETS DETECTED\",\n    \"book.invalid.tag\": \"* Invalid book tag *\",\n    \"block.minecraft.dead_tube_coral\": \"Dead Tube Coral\",\n    \"block.minecraft.dead_brain_coral\": \"Dead Brain Coral\",\n    \"block.minecraft.dead_bubble_coral\": \"Dead Bubble Coral\",\n    \"block.minecraft.dead_fire_coral\": \"Dead Fire Coral\",\n    \"block.minecraft.dead_horn_coral\": \"Dead Horn Coral\",\n    \"entity.minecraft.tropical_fish.predefined.0\": \"Anemone\",\n    \"entity.minecraft.tropical_fish.predefined.1\": \"Black Tang\",\n    \"entity.minecraft.tropical_fish.predefined.2\": \"Blue Tang\",\n    \"entity.minecraft.tropical_fish.predefined.3\": \"Butterflyfish\",\n    \"entity.minecraft.tropical_fish.predefined.4\": \"Cichlid\",\n    \"entity.minecraft.tropical_fish.predefined.5\": \"Clownfish\",\n    \"entity.minecraft.tropical_fish.predefined.6\": \"Cotton Candy Betta\",\n    \"entity.minecraft.tropical_fish.predefined.7\": \"Dottyback\",\n    \"entity.minecraft.tropical_fish.predefined.8\": \"Emperor Red Snapper\",\n    \"entity.minecraft.tropical_fish.predefined.9\": \"Goatfish\",\n    \"entity.minecraft.tropical_fish.predefined.10\": \"Moorish Idol\",\n    \"entity.minecraft.tropical_fish.predefined.11\": \"Ornate Butterflyfish\",\n    \"entity.minecraft.tropical_fish.predefined.12\": \"Parrotfish\",\n    \"entity.minecraft.tropical_fish.predefined.13\": \"Queen Angelfish\",\n    \"entity.minecraft.tropical_fish.predefined.14\": \"Red Cichlid\",\n    \"entity.minecraft.tropical_fish.predefined.15\": \"Red Lipped Blenny\",\n    \"entity.minecraft.tropical_fish.predefined.16\": \"Red Snapper\",\n    \"entity.minecraft.tropical_fish.predefined.17\": \"Threadfin\",\n    \"entity.minecraft.tropical_fish.predefined.18\": \"Tomato Clownfish\",\n    \"entity.minecraft.tropical_fish.predefined.19\": \"Triggerfish\",\n    \"entity.minecraft.tropical_fish.predefined.20\": \"Yellowtail Parrotfish\",\n    \"entity.minecraft.tropical_fish.predefined.21\": \"Yellow Tang\",\n    \"entity.minecraft.tropical_fish.type.flopper\": \"Flopper\",\n    \"entity.minecraft.tropical_fish.type.stripey\": \"Stripey\",\n    \"entity.minecraft.tropical_fish.type.glitter\": \"Glitter\",\n    \"entity.minecraft.tropical_fish.type.blockfish\": \"Blockfish\",\n    \"entity.minecraft.tropical_fish.type.betty\": \"Betty\",\n    \"entity.minecraft.tropical_fish.type.clayfish\": \"Clayfish\",\n    \"entity.minecraft.tropical_fish.type.kob\": \"Kob\",\n    \"entity.minecraft.tropical_fish.type.sunstreak\": \"SunStreak\",\n    \"entity.minecraft.tropical_fish.type.snooper\": \"Snooper\",\n    \"entity.minecraft.tropical_fish.type.dasher\": \"Dasher\",\n    \"entity.minecraft.tropical_fish.type.brinely\": \"Brinely\",\n    \"entity.minecraft.tropical_fish.type.spotty\": \"Spotty\",\n    \"death.attack.even_more_magic\": \"%1$s was killed by even more magic\",\n    \"death.attack.message_too_long\": \"Actually, message was too long to deliver fully. Sorry! Here's stripped version: %s\",\n    \"stat.minecraft.clean_shulker_box\": \"Shulker Box Cleaned\",\n    \"stat.minecraft.damage_dealt_absorbed\": \"Damage Dealt (Absorbed)\",\n    \"stat.minecraft.damage_dealt_resisted\": \"Damage Dealt (Resisted)\",\n    \"stat.minecraft.damage_blocked_by_shield\": \"Damage Blocked By Shield\",\n    \"stat.minecraft.damage_absorbed\": \"Damage Absorbed\",\n    \"stat.minecraft.damage_resisted\": \"Damage Resisted\",\n    \"commands.forceload.added.failure\": \"No chunks were marked for force loading\",\n    \"commands.forceload.added.single\": \"Marked chunk %s in %s to be force loaded\",\n    \"commands.forceload.added.multiple\": \"Marked %s chunks in %s from %s to %s to be force loaded\",\n    \"commands.forceload.query.success\": \"Chunk at %s in %s is marked for force loading\",\n    \"commands.forceload.query.failure\": \"Chunk at %s in %s is not marked for force loading\",\n    \"commands.forceload.list.single\": \"A force loaded chunk was found in %s at: %s\",\n    \"commands.forceload.list.multiple\": \"%s force loaded chunks were found in %s at: %s\",\n    \"commands.forceload.added.none\": \"No force loaded chunks were found in %s\",\n    \"commands.forceload.removed.all\": \"Unmarked all force loaded chunks in %s\",\n    \"commands.forceload.removed.failure\": \"No chunks were removed from force loading\",\n    \"commands.forceload.removed.single\": \"Unmarked chunk %s in %s for force loading\",\n    \"commands.forceload.removed.multiple\": \"Unmarked %s chunks in %s from %s to %s for force loading\",\n    \"commands.forceload.toobig\": \"Too many chunks in the specified area (maximum %s, specified %s)\",\n    \"argument.pos2d.incomplete\": \"Incomplete (expected 2 coordinates)\",\n    \"argument.pos3d.incomplete\": \"Incomplete (expected 3 coordinates)\",\n    \"argument.dimension.invalid\": \"Unknown dimension '%s'\",\n    \"color.minecraft.white\": \"White\",\n    \"color.minecraft.orange\": \"Orange\",\n    \"color.minecraft.magenta\": \"Magenta\",\n    \"color.minecraft.light_blue\": \"Light Blue\",\n    \"color.minecraft.yellow\": \"Yellow\",\n    \"color.minecraft.lime\": \"Lime\",\n    \"color.minecraft.pink\": \"Pink\",\n    \"color.minecraft.gray\": \"Gray\",\n    \"color.minecraft.light_gray\": \"Light Gray\",\n    \"color.minecraft.cyan\": \"Cyan\",\n    \"color.minecraft.purple\": \"Purple\",\n    \"color.minecraft.blue\": \"Blue\",\n    \"color.minecraft.brown\": \"Brown\",\n    \"color.minecraft.green\": \"Green\",\n    \"color.minecraft.red\": \"Red\",\n    \"color.minecraft.black\": \"Black\"\n  },\n  \"1.13\": {\n    \"gui.ok\": \"Ok\",\n    \"gui.proceed\": \"Proceed\",\n    \"gui.recipebook.toggleRecipes.smeltable\": \"Showing smeltable\",\n    \"menu.savingLevel\": \"Saving world\",\n    \"menu.working\": \"Working...\",\n    \"menu.savingChunks\": \"Saving chunks\",\n    \"menu.preparingSpawn\": \"Preparing spawn area\",\n    \"optimizeWorld.confirm.title\": \"Optimize world\",\n    \"optimizeWorld.confirm.description\": \"This will attempt to optimize your world by making sure all data is stored in the most recent game format. This can take a very long time, depending on your world. Once done, your world may play faster but will no longer be compatible with older versions of the game. Are you sure you wish to proceed?\",\n    \"optimizeWorld.title\": \"Optimizing World '%s'\",\n    \"optimizeWorld.stage.counting\": \"Counting chunks...\",\n    \"optimizeWorld.stage.upgrading\": \"Upgrading all chunks...\",\n    \"optimizeWorld.stage.finished\": \"Finishing up...\",\n    \"optimizeWorld.stage.failed\": \"Failed! :(\",\n    \"optimizeWorld.info.converted\": \"Upgraded chunks: %s\",\n    \"optimizeWorld.info.skipped\": \"Skipped chunks: %s\",\n    \"optimizeWorld.info.total\": \"Total chunks: %s\",\n    \"selectWorld.edit.backup\": \"Make Backup\",\n    \"selectWorld.edit.backupFolder\": \"Open Backups Folder\",\n    \"selectWorld.edit.backupCreated\": \"Backed up: %s\",\n    \"selectWorld.edit.backupSize\": \"size: %s MB\",\n    \"selectWorld.edit.optimize\": \"Optimize World\",\n    \"selectWorld.backupQuestion\": \"Do you really want to load this world?\",\n    \"selectWorld.backupWarning\": \"This world was last played in version %s; you are on snapshot %s. Please make a backup in case you experience world corruptions!\",\n    \"selectWorld.backupQuestion.customized\": \"Customized worlds are no longer supported\",\n    \"selectWorld.backupWarning.customized\": \"Unfortunately, we do not support customized worlds in this version of Minecraft. We can still load this world and keep everything the way it was, but any newly generated terrain will no longer be customized. We're sorry for the inconvenience!\",\n    \"selectWorld.backupJoinConfirmButton\": \"Backup and load\",\n    \"selectWorld.backupJoinSkipButton\": \"I know what I'm doing!\",\n    \"selectWorld.tooltip.unsupported\": \"This world is no longer supported and can only be played in %s\",\n    \"selectWorld.futureworld.error.title\": \"An error occured!\",\n    \"selectWorld.futureworld.error.text\": \"Something went wrong while trying to load a world from a future version. This was a risky operation to begin with, sorry it didn't work.\",\n    \"selectWorld.recreate.error.title\": \"An error occured!\",\n    \"selectWorld.recreate.error.text\": \"Something went wrong while trying to recreate a world.\",\n    \"selectWorld.recreate.customized.title\": \"Customized worlds are no longer supported\",\n    \"selectWorld.recreate.customized.text\": \"Customized worlds are no longer supported in this version of Minecraft. We can try to recreate it with the same seed and properties, but any terrain customizations will be lost. We're sorry for the inconvenience!\",\n    \"createWorld.customize.buffet.title\": \"Buffet world customization\",\n    \"createWorld.customize.buffet.generatortype\": \"World generator:\",\n    \"createWorld.customize.buffet.generator\": \"Please select a generator type\",\n    \"createWorld.customize.buffet.biome\": \"Please select a biome\",\n    \"createWorld.customize.custom.useOceanRuins\": \"Ocean Ruins\",\n    \"generator.buffet\": \"Buffet\",\n    \"multiplayer.message_not_delivered\": \"Can't deliver chat message, check server logs\",\n    \"multiplayer.status.finished\": \"Finished\",\n    \"multiplayer.status.request_handled\": \"Status requst has been handled\",\n    \"multiplayer.disconnect.banned.reason\": \"You are banned from this server.\\nReason: %s\",\n    \"multiplayer.disconnect.banned.expiration\": \"\\nYour ban will be removed on %s\",\n    \"multiplayer.disconnect.banned_ip.reason\": \"Your IP address is banned from this server.\\nReason: %s\",\n    \"multiplayer.disconnect.banned_ip.expiration\": \"\\nYour ban will be removed on %s\",\n    \"multiplayer.disconnect.not_whitelisted\": \"You are not white-listed on this server!\",\n    \"multiplayer.disconnect.server_full\": \"The server is full!\",\n    \"multiplayer.disconnect.name_taken\": \"That name is already taken\",\n    \"multiplayer.disconnect.unexpected_query_response\": \"Unexpected custom data from client\",\n    \"chat.coordinates\": \"%s, %s, %s\",\n    \"chat.coordinates.tooltip\": \"Click to teleport\",\n    \"connect.aborted\": \"Aborted\",\n    \"connect.negotiating\": \"Negotiating...\",\n    \"connect.encrypting\": \"Encrypting...\",\n    \"connect.joining\": \"Joining world...\",\n    \"options.biomeBlendRadius\": \"Biome Blend\",\n    \"options.autoSuggestCommands\": \"Command Suggestions\",\n    \"options.fullscreen.resolution\": \"Fullscreen Resolution\",\n    \"options.fullscreen.current\": \"Current\",\n    \"key.keyboard.unknown\": \"Not bound\",\n    \"key.keyboard.apostrophe\": \"'\",\n    \"key.keyboard.backslash\": \"\\\\\",\n    \"key.keyboard.backspace\": \"Backspace\",\n    \"key.keyboard.comma\": \",\",\n    \"key.keyboard.delete\": \"Delete\",\n    \"key.keyboard.end\": \"End\",\n    \"key.keyboard.enter\": \"Enter\",\n    \"key.keyboard.equal\": \"=\",\n    \"key.keyboard.escape\": \"Escape\",\n    \"key.keyboard.f1\": \"F1\",\n    \"key.keyboard.f2\": \"F2\",\n    \"key.keyboard.f3\": \"F3\",\n    \"key.keyboard.f4\": \"F4\",\n    \"key.keyboard.f5\": \"F5\",\n    \"key.keyboard.f6\": \"F6\",\n    \"key.keyboard.f7\": \"F7\",\n    \"key.keyboard.f8\": \"F8\",\n    \"key.keyboard.f9\": \"F9\",\n    \"key.keyboard.f10\": \"F10\",\n    \"key.keyboard.f11\": \"F11\",\n    \"key.keyboard.f12\": \"F12\",\n    \"key.keyboard.f13\": \"F13\",\n    \"key.keyboard.f14\": \"F14\",\n    \"key.keyboard.f15\": \"F15\",\n    \"key.keyboard.f16\": \"F16\",\n    \"key.keyboard.f17\": \"F17\",\n    \"key.keyboard.f18\": \"F18\",\n    \"key.keyboard.f19\": \"F19\",\n    \"key.keyboard.f20\": \"F20\",\n    \"key.keyboard.f21\": \"F21\",\n    \"key.keyboard.f22\": \"F22\",\n    \"key.keyboard.f23\": \"F23\",\n    \"key.keyboard.f24\": \"F24\",\n    \"key.keyboard.f25\": \"F25\",\n    \"key.keyboard.grave.accent\": \"`\",\n    \"key.keyboard.home\": \"Home\",\n    \"key.keyboard.insert\": \"Insert\",\n    \"key.keyboard.keypad.0\": \"Keypad 0\",\n    \"key.keyboard.keypad.1\": \"Keypad 1\",\n    \"key.keyboard.keypad.2\": \"Keypad 2\",\n    \"key.keyboard.keypad.3\": \"Keypad 3\",\n    \"key.keyboard.keypad.4\": \"Keypad 4\",\n    \"key.keyboard.keypad.5\": \"Keypad 5\",\n    \"key.keyboard.keypad.6\": \"Keypad 6\",\n    \"key.keyboard.keypad.7\": \"Keypad 7\",\n    \"key.keyboard.keypad.8\": \"Keypad 8\",\n    \"key.keyboard.keypad.9\": \"Keypad 9\",\n    \"key.keyboard.keypad.add\": \"Keypad +\",\n    \"key.keyboard.keypad.decimal\": \"Keypad Decimal\",\n    \"key.keyboard.keypad.enter\": \"Keypad Enter\",\n    \"key.keyboard.keypad.equal\": \"Keypad =\",\n    \"key.keyboard.keypad.multiply\": \"Keypad *\",\n    \"key.keyboard.keypad.divide\": \"Keypad /\",\n    \"key.keyboard.keypad.subtract\": \"Keypad -\",\n    \"key.keyboard.left.bracket\": \"[\",\n    \"key.keyboard.right.bracket\": \"]\",\n    \"key.keyboard.minus\": \"-\",\n    \"key.keyboard.num.lock\": \"Num Lock\",\n    \"key.keyboard.caps.lock\": \"Caps Lock\",\n    \"key.keyboard.scroll.lock\": \"Scroll Lock\",\n    \"key.keyboard.page.down\": \"Page Down\",\n    \"key.keyboard.page.up\": \"Page Up\",\n    \"key.keyboard.pause\": \"Pause\",\n    \"key.keyboard.period\": \".\",\n    \"key.keyboard.left.control\": \"Left Control\",\n    \"key.keyboard.right.control\": \"Right Control\",\n    \"key.keyboard.left.alt\": \"Left Alt\",\n    \"key.keyboard.right.alt\": \"Right Alt\",\n    \"key.keyboard.left.shift\": \"Left Shift\",\n    \"key.keyboard.right.shift\": \"Right Shift\",\n    \"key.keyboard.left.win\": \"Left Win\",\n    \"key.keyboard.right.win\": \"Right Win\",\n    \"key.keyboard.semicolon\": \";\",\n    \"key.keyboard.slash\": \"/\",\n    \"key.keyboard.space\": \"Space\",\n    \"key.keyboard.tab\": \"Tab\",\n    \"key.keyboard.up\": \"Up Arrow\",\n    \"key.keyboard.down\": \"Down Arrow\",\n    \"key.keyboard.left\": \"Left Arrow\",\n    \"key.keyboard.right\": \"Right Arrow\",\n    \"key.keyboard.menu\": \"Menu\",\n    \"key.keyboard.print.screen\": \"Print Screen\",\n    \"key.keyboard.world.1\": \"World 1\",\n    \"key.keyboard.world.2\": \"World 2\",\n    \"resourcePack.server.name\": \"World Specific Resources\",\n    \"block.minecraft.oak_planks\": \"Oak Planks\",\n    \"block.minecraft.spruce_planks\": \"Spruce Planks\",\n    \"block.minecraft.birch_planks\": \"Birch Planks\",\n    \"block.minecraft.jungle_planks\": \"Jungle Planks\",\n    \"block.minecraft.acacia_planks\": \"Acacia Planks\",\n    \"block.minecraft.dark_oak_planks\": \"Dark Oak Planks\",\n    \"block.minecraft.flowing_water\": \"Flowing Water\",\n    \"block.minecraft.flowing_lava\": \"Flowing Lava\",\n    \"block.minecraft.cut_sandstone\": \"Cut Sandstone\",\n    \"block.minecraft.cut_red_sandstone\": \"Cut Red Sandstone\",\n    \"block.minecraft.oak_log\": \"Oak Log\",\n    \"block.minecraft.spruce_log\": \"Spruce Log\",\n    \"block.minecraft.birch_log\": \"Birch Log\",\n    \"block.minecraft.jungle_log\": \"Jungle Log\",\n    \"block.minecraft.acacia_log\": \"Acacia Log\",\n    \"block.minecraft.dark_oak_log\": \"Dark Oak Log\",\n    \"block.minecraft.stripped_oak_log\": \"Stripped Oak Log\",\n    \"block.minecraft.stripped_spruce_log\": \"Stripped Spruce Log\",\n    \"block.minecraft.stripped_birch_log\": \"Stripped Birch Log\",\n    \"block.minecraft.stripped_jungle_log\": \"Stripped Jungle Log\",\n    \"block.minecraft.stripped_acacia_log\": \"Stripped Acacia Log\",\n    \"block.minecraft.stripped_dark_oak_log\": \"Stripped Dark Oak Log\",\n    \"block.minecraft.stripped_oak_wood\": \"Stripped Oak Wood\",\n    \"block.minecraft.stripped_spruce_wood\": \"Stripped Spruce Wood\",\n    \"block.minecraft.stripped_birch_wood\": \"Stripped Birch Wood\",\n    \"block.minecraft.stripped_jungle_wood\": \"Stripped Jungle Wood\",\n    \"block.minecraft.stripped_acacia_wood\": \"Stripped Acacia Wood\",\n    \"block.minecraft.stripped_dark_oak_wood\": \"Stripped Dark Oak Wood\",\n    \"block.minecraft.kelp_plant\": \"Kelp Plant\",\n    \"block.minecraft.kelp\": \"Kelp\",\n    \"block.minecraft.dried_kelp_block\": \"Dried Kelp Block\",\n    \"block.minecraft.tall_grass\": \"Tall Grass\",\n    \"block.minecraft.tall_seagrass\": \"Tall Seagrass\",\n    \"block.minecraft.seagrass\": \"Seagrass\",\n    \"block.minecraft.sea_pickle\": \"Sea Pickle\",\n    \"block.minecraft.brown_mushroom\": \"Brown Mushroom\",\n    \"block.minecraft.red_mushroom_block\": \"Red Mushroom Block\",\n    \"block.minecraft.brown_mushroom_block\": \"Brown Mushroom Block\",\n    \"block.minecraft.mushroom_stem\": \"Mushroom Stem\",\n    \"block.minecraft.smooth_stone\": \"Smooth Stone\",\n    \"block.minecraft.smooth_quartz\": \"Smooth Quartz\",\n    \"block.minecraft.petrified_oak_slab\": \"Petrified Oak Slab\",\n    \"block.minecraft.brick_slab\": \"Brick Slab\",\n    \"block.minecraft.stone_brick_slab\": \"Stone Brick Slab\",\n    \"block.minecraft.oak_slab\": \"Oak Slab\",\n    \"block.minecraft.spruce_slab\": \"Spruce Slab\",\n    \"block.minecraft.birch_slab\": \"Birch Slab\",\n    \"block.minecraft.jungle_slab\": \"Jungle Slab\",\n    \"block.minecraft.acacia_slab\": \"Acacia Slab\",\n    \"block.minecraft.dark_oak_slab\": \"Dark Oak Slab\",\n    \"block.minecraft.dark_prismarine_slab\": \"Dark Prismarine Slab\",\n    \"block.minecraft.prismarine_slab\": \"Prismarine Slab\",\n    \"block.minecraft.prismarine_brick_slab\": \"Prismarine Brick Slab\",\n    \"block.minecraft.mossy_cobblestone\": \"Mossy Cobblestone\",\n    \"block.minecraft.wall_torch\": \"Wall Torch\",\n    \"block.minecraft.spawner\": \"Spawner\",\n    \"block.minecraft.oak_stairs\": \"Oak Stairs\",\n    \"block.minecraft.spruce_stairs\": \"Spruce Stairs\",\n    \"block.minecraft.birch_stairs\": \"Birch Stairs\",\n    \"block.minecraft.jungle_stairs\": \"Jungle Stairs\",\n    \"block.minecraft.acacia_stairs\": \"Acacia Stairs\",\n    \"block.minecraft.dark_oak_stairs\": \"Dark Oak Stairs\",\n    \"block.minecraft.dark_prismarine_stairs\": \"Dark Prismarine Stairs\",\n    \"block.minecraft.prismarine_stairs\": \"Prismarine Stairs\",\n    \"block.minecraft.prismarine_brick_stairs\": \"Prismarine Brick Stairs\",\n    \"block.minecraft.wheat\": \"Wheat Crops\",\n    \"block.minecraft.sign\": \"Sign\",\n    \"block.minecraft.wall_sign\": \"Wall Sign\",\n    \"block.minecraft.oak_pressure_plate\": \"Oak Pressure Plate\",\n    \"block.minecraft.spruce_pressure_plate\": \"Spruce Pressure Plate\",\n    \"block.minecraft.birch_pressure_plate\": \"Birch Pressure Plate\",\n    \"block.minecraft.jungle_pressure_plate\": \"Jungle Pressure Plate\",\n    \"block.minecraft.acacia_pressure_plate\": \"Acacia Pressure Plate\",\n    \"block.minecraft.dark_oak_pressure_plate\": \"Dark Oak Pressure Plate\",\n    \"block.minecraft.light_weighted_pressure_plate\": \"Light Weighted Pressure Plate\",\n    \"block.minecraft.heavy_weighted_pressure_plate\": \"Heavy Weighted Pressure Plate\",\n    \"block.minecraft.redstone_wall_torch\": \"Redstone Wall Torch\",\n    \"block.minecraft.stone_button\": \"Stone Button\",\n    \"block.minecraft.oak_button\": \"Oak Button\",\n    \"block.minecraft.spruce_button\": \"Spruce Button\",\n    \"block.minecraft.birch_button\": \"Birch Button\",\n    \"block.minecraft.jungle_button\": \"Jungle Button\",\n    \"block.minecraft.acacia_button\": \"Acacia Button\",\n    \"block.minecraft.dark_oak_button\": \"Dark Oak Button\",\n    \"block.minecraft.blue_ice\": \"Blue Ice\",\n    \"block.minecraft.sugar_cane\": \"Sugar Cane\",\n    \"block.minecraft.attached_pumpkin_stem\": \"Attached Pumpkin Stem\",\n    \"block.minecraft.carved_pumpkin\": \"Carved Pumpkin\",\n    \"block.minecraft.nether_portal\": \"Nether Portal\",\n    \"block.minecraft.bed\": \"Bed\",\n    \"block.minecraft.bed.no_sleep\": \"You can sleep only at night and during thunderstorms\",\n    \"block.minecraft.bed.too_far_away\": \"You may not rest now; the bed is too far away\",\n    \"block.minecraft.bed.not_safe\": \"You may not rest now; there are monsters nearby\",\n    \"block.minecraft.oak_trapdoor\": \"Oak Trapdoor\",\n    \"block.minecraft.spruce_trapdoor\": \"Spruce Trapdoor\",\n    \"block.minecraft.birch_trapdoor\": \"Birch Trapdoor\",\n    \"block.minecraft.jungle_trapdoor\": \"Jungle Trapdoor\",\n    \"block.minecraft.acacia_trapdoor\": \"Acacia Trapdoor\",\n    \"block.minecraft.dark_oak_trapdoor\": \"Dark Oak Trapdoor\",\n    \"block.minecraft.infested_stone\": \"Infested Stone\",\n    \"block.minecraft.infested_cobblestone\": \"Infested Cobblestone\",\n    \"block.minecraft.infested_stone_bricks\": \"Infested Stone Bricks\",\n    \"block.minecraft.infested_mossy_stone_bricks\": \"Infested Mossy Stone Bricks\",\n    \"block.minecraft.infested_cracked_stone_bricks\": \"Infested Cracked Stone Bricks\",\n    \"block.minecraft.infested_chiseled_stone_bricks\": \"Infested Chiseled Stone Bricks\",\n    \"block.minecraft.nether_bricks\": \"Nether Bricks\",\n    \"block.minecraft.enchanting_table\": \"Enchanting Table\",\n    \"block.minecraft.chipped_anvil\": \"Chipped Anvil\",\n    \"block.minecraft.damaged_anvil\": \"Damaged Anvil\",\n    \"block.minecraft.end_portal_frame\": \"End Portal Frame\",\n    \"block.minecraft.daylight_detector\": \"Daylight Detector\",\n    \"block.minecraft.quartz_pillar\": \"Quartz Pillar\",\n    \"block.minecraft.red_nether_bricks\": \"Red Nether Bricks\",\n    \"block.minecraft.turtle_egg\": \"Turtle Egg\",\n    \"block.minecraft.two_turtle_eggs\": \"Two Turtle Eggs\",\n    \"block.minecraft.three_turtle_eggs\": \"Three Turtle Eggs\",\n    \"block.minecraft.four_turtle_eggs\": \"Four Turtle Eggs\",\n    \"block.minecraft.banner\": \"Banner\",\n    \"block.minecraft.wall_banner\": \"Wall Banner\",\n    \"block.minecraft.piston_head\": \"Piston Head\",\n    \"block.minecraft.moving_piston\": \"Moving Piston\",\n    \"block.minecraft.red_mushroom\": \"Red Mushroom\",\n    \"block.minecraft.snow_block\": \"Snow Block\",\n    \"block.minecraft.attached_melon_stem\": \"Attached Melon Stem\",\n    \"block.minecraft.melon_stem\": \"Melon Stem\",\n    \"block.minecraft.potted_oak_sapling\": \"Potted Oak Sapling\",\n    \"block.minecraft.potted_spruce_sapling\": \"Potted Spruce Sapling\",\n    \"block.minecraft.potted_birch_sapling\": \"Potted Birch Sapling\",\n    \"block.minecraft.potted_jungle_sapling\": \"Potted Jungle Sapling\",\n    \"block.minecraft.potted_acacia_sapling\": \"Potted Acacia Sapling\",\n    \"block.minecraft.potted_dark_oak_sapling\": \"Potted Dark Oak Sapling\",\n    \"block.minecraft.potted_fern\": \"Potted Fern\",\n    \"block.minecraft.potted_dandelion\": \"Potted Dandelion\",\n    \"block.minecraft.potted_poppy\": \"Potted Poppy\",\n    \"block.minecraft.potted_blue_orchid\": \"Potted Blue Orchid\",\n    \"block.minecraft.potted_allium\": \"Potted Allium\",\n    \"block.minecraft.potted_azure_bluet\": \"Potted Azure Bluet\",\n    \"block.minecraft.potted_red_tulip\": \"Potted Red Tulip\",\n    \"block.minecraft.potted_orange_tulip\": \"Potted Orange Tulip\",\n    \"block.minecraft.potted_white_tulip\": \"Potted White Tulip\",\n    \"block.minecraft.potted_pink_tulip\": \"Potted Pink Tulip\",\n    \"block.minecraft.potted_oxeye_daisy\": \"Potted Oxeye Daisy\",\n    \"block.minecraft.potted_red_mushroom\": \"Potted Red Mushroom\",\n    \"block.minecraft.potted_brown_mushroom\": \"Potted Brown Mushroom\",\n    \"block.minecraft.potted_dead_bush\": \"Potted Dead Bush\",\n    \"block.minecraft.potted_cactus\": \"Potted Cactus\",\n    \"block.minecraft.skeleton_wall_skull\": \"Skeleton Wall Skull\",\n    \"block.minecraft.wither_skeleton_wall_skull\": \"Wither Skeleton Wall Skull\",\n    \"block.minecraft.zombie_wall_head\": \"Zombie Wall Head\",\n    \"block.minecraft.player_wall_head\": \"Player Wall Head\",\n    \"block.minecraft.player_head\": \"Player Head\",\n    \"block.minecraft.creeper_wall_head\": \"Creeper Wall Head\",\n    \"block.minecraft.dragon_wall_head\": \"Dragon Wall Head\",\n    \"block.minecraft.end_gateway\": \"End Gateway\",\n    \"block.minecraft.void_air\": \"Void Air\",\n    \"block.minecraft.cave_air\": \"Cave Air\",\n    \"block.minecraft.bubble_column\": \"Bubble Column\",\n    \"block.minecraft.dead_tube_coral_block\": \"Dead Tube Coral Block\",\n    \"block.minecraft.dead_brain_coral_block\": \"Dead Brain Coral Block\",\n    \"block.minecraft.dead_bubble_coral_block\": \"Dead Bubble Coral Block\",\n    \"block.minecraft.dead_fire_coral_block\": \"Dead Fire Coral Block\",\n    \"block.minecraft.dead_horn_coral_block\": \"Dead Horn Coral Block\",\n    \"block.minecraft.tube_coral_block\": \"Tube Coral Block\",\n    \"block.minecraft.brain_coral_block\": \"Brain Coral Block\",\n    \"block.minecraft.bubble_coral_block\": \"Bubble Coral Block\",\n    \"block.minecraft.fire_coral_block\": \"Fire Coral Block\",\n    \"block.minecraft.horn_coral_block\": \"Horn Coral Block\",\n    \"block.minecraft.tube_coral\": \"Tube Coral\",\n    \"block.minecraft.brain_coral\": \"Brain Coral\",\n    \"block.minecraft.bubble_coral\": \"Bubble Coral\",\n    \"block.minecraft.fire_coral\": \"Fire Coral\",\n    \"block.minecraft.horn_coral\": \"Horn Coral\",\n    \"block.minecraft.tube_coral_fan\": \"Tube Coral Fan\",\n    \"block.minecraft.brain_coral_fan\": \"Brain Coral Fan\",\n    \"block.minecraft.bubble_coral_fan\": \"Bubble Coral Fan\",\n    \"block.minecraft.fire_coral_fan\": \"Fire Coral Fan\",\n    \"block.minecraft.horn_coral_fan\": \"Horn Coral Fan\",\n    \"block.minecraft.dead_tube_coral_fan\": \"Dead Tube Coral Fan\",\n    \"block.minecraft.dead_brain_coral_fan\": \"Dead Brain Coral Fan\",\n    \"block.minecraft.dead_bubble_coral_fan\": \"Dead Bubble Coral Fan\",\n    \"block.minecraft.dead_fire_coral_fan\": \"Dead Fire Coral Fan\",\n    \"block.minecraft.dead_horn_coral_fan\": \"Dead Horn Coral Fan\",\n    \"block.minecraft.tube_coral_wall_fan\": \"Tube Coral Wall Fan\",\n    \"block.minecraft.brain_coral_wall_fan\": \"Brain Coral Wall Fan\",\n    \"block.minecraft.bubble_coral_wall_fan\": \"Bubble Coral Wall Fan\",\n    \"block.minecraft.fire_coral_wall_fan\": \"Fire Coral Wall Fan\",\n    \"block.minecraft.horn_coral_wall_fan\": \"Horn Coral Wall Fan\",\n    \"block.minecraft.dead_tube_coral_wall_fan\": \"Dead Tube Coral Wall Fan\",\n    \"block.minecraft.dead_brain_coral_wall_fan\": \"Dead Brain Coral Wall Fan\",\n    \"block.minecraft.dead_bubble_coral_wall_fan\": \"Dead Bubble Coral Wall Fan\",\n    \"block.minecraft.dead_fire_coral_wall_fan\": \"Dead Fire Coral Wall Fan\",\n    \"block.minecraft.dead_horn_coral_wall_fan\": \"Dead Horn Coral Wall Fan\",\n    \"block.minecraft.conduit\": \"Conduit\",\n    \"item.minecraft.dried_kelp\": \"Dried Kelp\",\n    \"item.minecraft.wheat_seeds\": \"Wheat Seeds\",\n    \"item.minecraft.melon_slice\": \"Melon Slice\",\n    \"item.minecraft.chainmail_helmet\": \"Chainmail Helmet\",\n    \"item.minecraft.chainmail_chestplate\": \"Chainmail Chestplate\",\n    \"item.minecraft.chainmail_leggings\": \"Chainmail Leggings\",\n    \"item.minecraft.chainmail_boots\": \"Chainmail Boots\",\n    \"item.minecraft.enchanted_golden_apple\": \"Enchanted Golden Apple\",\n    \"item.minecraft.sign\": \"Sign\",\n    \"item.minecraft.pufferfish_bucket\": \"Bucket of Pufferfish\",\n    \"item.minecraft.salmon_bucket\": \"Bucket of Salmon\",\n    \"item.minecraft.cod_bucket\": \"Bucket of Cod\",\n    \"item.minecraft.tropical_fish_bucket\": \"Bucket of Tropical Fish\",\n    \"item.minecraft.milk_bucket\": \"Milk Bucket\",\n    \"item.minecraft.clay_ball\": \"Clay\",\n    \"item.minecraft.cod\": \"Raw Cod\",\n    \"item.minecraft.tropical_fish\": \"Tropical Fish\",\n    \"item.minecraft.cooked_cod\": \"Cooked Cod\",\n    \"item.minecraft.music_disc_cat\": \"Music Disc\",\n    \"item.minecraft.music_disc_blocks\": \"Music Disc\",\n    \"item.minecraft.music_disc_chirp\": \"Music Disc\",\n    \"item.minecraft.music_disc_far\": \"Music Disc\",\n    \"item.minecraft.music_disc_mall\": \"Music Disc\",\n    \"item.minecraft.music_disc_mellohi\": \"Music Disc\",\n    \"item.minecraft.music_disc_stal\": \"Music Disc\",\n    \"item.minecraft.music_disc_strad\": \"Music Disc\",\n    \"item.minecraft.music_disc_ward\": \"Music Disc\",\n    \"item.minecraft.music_disc_11\": \"Music Disc\",\n    \"item.minecraft.music_disc_wait\": \"Music Disc\",\n    \"item.minecraft.nether_wart\": \"Nether Wart\",\n    \"item.minecraft.cauldron\": \"Cauldron\",\n    \"item.minecraft.brewing_stand\": \"Brewing Stand\",\n    \"item.minecraft.glistering_melon_slice\": \"Glistering Melon Slice\",\n    \"item.minecraft.bat_spawn_egg\": \"Bat Spawn Egg\",\n    \"item.minecraft.blaze_spawn_egg\": \"Blaze Spawn Egg\",\n    \"item.minecraft.cave_spider_spawn_egg\": \"Cave Spider Spawn Egg\",\n    \"item.minecraft.chicken_spawn_egg\": \"Chicken Spawn Egg\",\n    \"item.minecraft.cod_spawn_egg\": \"Cod Spawn Egg\",\n    \"item.minecraft.cow_spawn_egg\": \"Cow Spawn Egg\",\n    \"item.minecraft.creeper_spawn_egg\": \"Creeper Spawn Egg\",\n    \"item.minecraft.dolphin_spawn_egg\": \"Dolphin Spawn Egg\",\n    \"item.minecraft.donkey_spawn_egg\": \"Donkey Spawn Egg\",\n    \"item.minecraft.drowned_spawn_egg\": \"Drowned Spawn Egg\",\n    \"item.minecraft.elder_guardian_spawn_egg\": \"Elder Guardian Spawn Egg\",\n    \"item.minecraft.enderman_spawn_egg\": \"Enderman Spawn Egg\",\n    \"item.minecraft.endermite_spawn_egg\": \"Endermite Spawn Egg\",\n    \"item.minecraft.evoker_spawn_egg\": \"Evoker Spawn Egg\",\n    \"item.minecraft.ghast_spawn_egg\": \"Ghast Spawn Egg\",\n    \"item.minecraft.guardian_spawn_egg\": \"Guardian Spawn Egg\",\n    \"item.minecraft.horse_spawn_egg\": \"Horse Spawn Egg\",\n    \"item.minecraft.husk_spawn_egg\": \"Husk Spawn Egg\",\n    \"item.minecraft.llama_spawn_egg\": \"Llama Spawn Egg\",\n    \"item.minecraft.magma_cube_spawn_egg\": \"Magma Cube Spawn Egg\",\n    \"item.minecraft.mooshroom_spawn_egg\": \"Mooshroom Spawn Egg\",\n    \"item.minecraft.mule_spawn_egg\": \"Mule Spawn Egg\",\n    \"item.minecraft.ocelot_spawn_egg\": \"Ocelot Spawn Egg\",\n    \"item.minecraft.parrot_spawn_egg\": \"Parrot Spawn Egg\",\n    \"item.minecraft.pig_spawn_egg\": \"Pig Spawn Egg\",\n    \"item.minecraft.phantom_spawn_egg\": \"Phantom Spawn Egg\",\n    \"item.minecraft.polar_bear_spawn_egg\": \"Polar Bear Spawn Egg\",\n    \"item.minecraft.pufferfish_spawn_egg\": \"Pufferfish Spawn Egg\",\n    \"item.minecraft.rabbit_spawn_egg\": \"Rabbit Spawn Egg\",\n    \"item.minecraft.salmon_spawn_egg\": \"Salmon Spawn Egg\",\n    \"item.minecraft.sheep_spawn_egg\": \"Sheep Spawn Egg\",\n    \"item.minecraft.shulker_spawn_egg\": \"Shulker Spawn Egg\",\n    \"item.minecraft.silverfish_spawn_egg\": \"Silverfish Spawn Egg\",\n    \"item.minecraft.skeleton_spawn_egg\": \"Skeleton Spawn Egg\",\n    \"item.minecraft.skeleton_horse_spawn_egg\": \"Skeleton Horse Spawn Egg\",\n    \"item.minecraft.slime_spawn_egg\": \"Slime Spawn Egg\",\n    \"item.minecraft.spider_spawn_egg\": \"Spider Spawn Egg\",\n    \"item.minecraft.squid_spawn_egg\": \"Squid Spawn Egg\",\n    \"item.minecraft.stray_spawn_egg\": \"Stray Spawn Egg\",\n    \"item.minecraft.tropical_fish_spawn_egg\": \"Tropical Fish Spawn Egg\",\n    \"item.minecraft.turtle_spawn_egg\": \"Turtle Spawn Egg\",\n    \"item.minecraft.vex_spawn_egg\": \"Vex Spawn Egg\",\n    \"item.minecraft.villager_spawn_egg\": \"Villager Spawn Egg\",\n    \"item.minecraft.vindicator_spawn_egg\": \"Vindicator Spawn Egg\",\n    \"item.minecraft.witch_spawn_egg\": \"Witch Spawn Egg\",\n    \"item.minecraft.wither_skeleton_spawn_egg\": \"Wither Skeleton Spawn Egg\",\n    \"item.minecraft.wolf_spawn_egg\": \"Wolf Spawn Egg\",\n    \"item.minecraft.zombie_spawn_egg\": \"Zombie Spawn Egg\",\n    \"item.minecraft.zombie_horse_spawn_egg\": \"Zombie Horse Spawn Egg\",\n    \"item.minecraft.zombie_pigman_spawn_egg\": \"Zombie Pigman Spawn Egg\",\n    \"item.minecraft.zombie_villager_spawn_egg\": \"Zombie Villager Spawn Egg\",\n    \"item.minecraft.flower_pot\": \"Flower Pot\",\n    \"item.minecraft.skeleton_skull\": \"Skeleton Skull\",\n    \"item.minecraft.wither_skeleton_skull\": \"Wither Skeleton Skull\",\n    \"item.minecraft.zombie_head\": \"Zombie Head\",\n    \"item.minecraft.creeper_head\": \"Creeper Head\",\n    \"item.minecraft.dragon_head\": \"Dragon Head\",\n    \"item.minecraft.golden_horse_armor\": \"Golden Horse Armor\",\n    \"item.minecraft.debug_stick\": \"Debug Stick\",\n    \"item.minecraft.debug_stick.empty\": \"%s has no properties\",\n    \"item.minecraft.debug_stick.update\": \"\\\"%s\\\" to %s\",\n    \"item.minecraft.debug_stick.select\": \"selected \\\"%s\\\" (%s)\",\n    \"item.minecraft.trident\": \"Trident\",\n    \"item.minecraft.scute\": \"Scute\",\n    \"item.minecraft.turtle_helmet\": \"Turtle Shell\",\n    \"item.minecraft.phantom_membrane\": \"Phantom Membrane\",\n    \"item.minecraft.nautilus_shell\": \"Nautilus Shell\",\n    \"item.minecraft.heart_of_the_sea\": \"Heart of the Sea\",\n    \"structure_block.invalid_structure_name\": \"Invalid structure name '%s'\",\n    \"filled_map.buried_treasure\": \"Buried Treasure Map\",\n    \"filled_map.id\": \"Id #%s\",\n    \"entity.minecraft.area_effect_cloud\": \"Area Effect Cloud\",\n    \"entity.minecraft.armor_stand\": \"Armor Stand\",\n    \"entity.minecraft.arrow\": \"Arrow\",\n    \"entity.minecraft.chest_minecart\": \"Minecart with Chest\",\n    \"entity.minecraft.command_block_minecart\": \"Minecart with Command Block\",\n    \"entity.minecraft.cod\": \"Cod\",\n    \"entity.minecraft.dolphin\": \"Dolphin\",\n    \"entity.minecraft.drowned\": \"Drowned\",\n    \"entity.minecraft.egg\": \"Thrown Egg\",\n    \"entity.minecraft.end_crystal\": \"End Crystal\",\n    \"entity.minecraft.ender_pearl\": \"Thrown Ender Pearl\",\n    \"entity.minecraft.evoker_fangs\": \"Evoker Fangs\",\n    \"entity.minecraft.eye_of_ender\": \"Eye of Ender\",\n    \"entity.minecraft.firework_rocket\": \"Firework Rocket\",\n    \"entity.minecraft.fishing_bobber\": \"Fishing Bobber\",\n    \"entity.minecraft.furnace_minecart\": \"Minecart with Furnace\",\n    \"entity.minecraft.hopper_minecart\": \"Minecart with Hopper\",\n    \"entity.minecraft.item_frame\": \"Item Frame\",\n    \"entity.minecraft.leash_knot\": \"Leash Knot\",\n    \"entity.minecraft.lightning_bolt\": \"Lightning Bolt\",\n    \"entity.minecraft.llama_spit\": \"Llama Spit\",\n    \"entity.minecraft.minecart\": \"Minecart\",\n    \"entity.minecraft.painting\": \"Painting\",\n    \"entity.minecraft.phantom\": \"Phantom\",\n    \"entity.minecraft.player\": \"Player\",\n    \"entity.minecraft.potion\": \"Potion\",\n    \"entity.minecraft.pufferfish\": \"Pufferfish\",\n    \"entity.minecraft.salmon\": \"Salmon\",\n    \"entity.minecraft.shulker_bullet\": \"Shulker Bullet\",\n    \"entity.minecraft.snowball\": \"Snowball\",\n    \"entity.minecraft.spawner_minecart\": \"Minecart with Spawner\",\n    \"entity.minecraft.spectral_arrow\": \"Spectral Arrow\",\n    \"entity.minecraft.tnt\": \"Primed TNT\",\n    \"entity.minecraft.tnt_minecart\": \"Minecart with TNT\",\n    \"entity.minecraft.trident\": \"Trident\",\n    \"entity.minecraft.tropical_fish\": \"Tropical Fish\",\n    \"entity.minecraft.turtle\": \"Turtle\",\n    \"entity.minecraft.villager.tool_smith\": \"Tool Smith\",\n    \"entity.minecraft.villager.weapon_smith\": \"Weapon Smith\",\n    \"entity.minecraft.wither_skull\": \"Wither Skull\",\n    \"entity.minecraft.experience_bottle\": \"Thrown Bottle o' Enchanting\",\n    \"death.attack.lightningBolt.player\": \"%1$s was struck by lightning whilst fighting %2$s\",\n    \"death.attack.inWall.player\": \"%1$s suffocated in a wall whilst fighting %2$s\",\n    \"death.attack.cramming.player\": \"%1$s was squashed by %2$s\",\n    \"death.attack.starve.player\": \"%1$s starved to death whilst fighting %2$s\",\n    \"death.attack.generic.player\": \"%1$s died because of %2$s\",\n    \"death.attack.explosion.player.item\": \"%1$s was blown up by %2$s using %3$s\",\n    \"death.attack.wither.player\": \"%1$s withered away whilst fighting %2$s\",\n    \"death.attack.anvil.player\": \"%1$s was squashed by a falling anvil whilst fighting %2$s\",\n    \"death.attack.fallingBlock.player\": \"%1$s was squashed by a falling block whilst fighting %2$s\",\n    \"death.attack.thorns.item\": \"%1$s was killed by %3$s trying to hurt %2$s\",\n    \"death.attack.trident\": \"%1$s was impaled by %2$s\",\n    \"death.attack.trident.item\": \"%1$s was impaled by %2$s with %3$s\",\n    \"death.attack.fall.player\": \"%1$s hit the ground too hard whilst trying to escape %2$s\",\n    \"death.attack.outOfWorld.player\": \"%1$s didn't want to live in the same world as %2$s\",\n    \"death.attack.dragonBreath.player\": \"%1$s was roasted in dragon breath by %2$s\",\n    \"death.attack.flyIntoWall.player\": \"%1$s experienced kinetic energy whilst trying to escape %2$s\",\n    \"death.attack.fireworks.player\": \"%1$s went off with a bang whilst fighting %2$s\",\n    \"death.attack.netherBed.message\": \"%1$s was killed by %2$s\",\n    \"death.attack.netherBed.link\": \"Intentional Game Design\",\n    \"effect.minecraft.wither\": \"Wither\",\n    \"effect.minecraft.saturation\": \"Saturation\",\n    \"effect.minecraft.slow_falling\": \"Slow Falling\",\n    \"effect.minecraft.conduit_power\": \"Conduit Power\",\n    \"effect.minecraft.dolphins_grace\": \"Dolphin's Grace\",\n    \"item.minecraft.tipped_arrow.effect.mundane\": \"Tipped Arrow\",\n    \"item.minecraft.tipped_arrow.effect.thick\": \"Tipped Arrow\",\n    \"item.minecraft.tipped_arrow.effect.awkward\": \"Tipped Arrow\",\n    \"item.minecraft.tipped_arrow.effect.turtle_master\": \"Arrow of the Turtle Master\",\n    \"item.minecraft.tipped_arrow.effect.slow_falling\": \"Arrow of Slow Falling\",\n    \"item.minecraft.potion.effect.turtle_master\": \"Potion of the Turtle Master\",\n    \"item.minecraft.potion.effect.slow_falling\": \"Potion of Slow Falling\",\n    \"item.minecraft.splash_potion.effect.turtle_master\": \"Splash Potion of the Turtle Master\",\n    \"item.minecraft.splash_potion.effect.slow_falling\": \"Splash Potion of Slow Falling\",\n    \"item.minecraft.lingering_potion.effect.turtle_master\": \"Lingering Potion of the Turtle Master\",\n    \"item.minecraft.lingering_potion.effect.slow_falling\": \"Lingering Potion of Slow Falling\",\n    \"enchantment.minecraft.loyalty\": \"Loyalty\",\n    \"enchantment.minecraft.impaling\": \"Impaling\",\n    \"enchantment.minecraft.riptide\": \"Riptide\",\n    \"enchantment.minecraft.channeling\": \"Channeling\",\n    \"stat_type.minecraft.broken\": \"Times Broken\",\n    \"stat.minecraft.walk_under_water_one_cm\": \"Distance Walked under Water\",\n    \"stat.minecraft.play_record\": \"Music Discs Played\",\n    \"stat.minecraft.walk_on_water_one_cm\": \"Distance Walked on Water\",\n    \"stat.minecraft.time_since_rest\": \"Since Last Rest\",\n    \"subtitles.block.bubble_column.bubble_pop\": \"Bubbles pop\",\n    \"subtitles.block.bubble_column.upwards_ambient\": \"Bubbles flow\",\n    \"subtitles.block.bubble_column.upwards_inside\": \"Bubbles woosh\",\n    \"subtitles.block.bubble_column.whirlpool_ambient\": \"Bubbles whirl\",\n    \"subtitles.block.bubble_column.whirlpool_inside\": \"Bubbles zoom\",\n    \"subtitles.entity.fishing_bobber.splash\": \"Fishing Bobber splashes\",\n    \"subtitles.entity.parrot.imitate.drowned\": \"Parrot gurgles\",\n    \"subtitles.entity.parrot.imitate.illusioner\": \"Parrot murmurs\",\n    \"subtitles.entity.parrot.imitate.phantom\": \"Parrot screeches\",\n    \"subtitles.entity.cod.death\": \"Cod dies\",\n    \"subtitles.entity.cod.flop\": \"Cod flops\",\n    \"subtitles.entity.cod.hurt\": \"Cod hurts\",\n    \"subtitles.entity.dolphin.ambient\": \"Dolphin chirps\",\n    \"subtitles.entity.dolphin.ambient_water\": \"Dolphin whistles\",\n    \"subtitles.entity.dolphin.attack\": \"Dolphin attacks\",\n    \"subtitles.entity.dolphin.death\": \"Dolphin dies\",\n    \"subtitles.entity.dolphin.eat\": \"Dolphin eats\",\n    \"subtitles.entity.dolphin.hurt\": \"Dolphin hurts\",\n    \"subtitles.entity.dolphin.jump\": \"Dolphin jumps\",\n    \"subtitles.entity.dolphin.play\": \"Dolphin plays\",\n    \"subtitles.entity.dolphin.splash\": \"Dolphin splashes\",\n    \"subtitles.entity.dolphin.swim\": \"Dolphin swims\",\n    \"subtitles.entity.drowned.ambient\": \"Drowned gurgles\",\n    \"subtitles.entity.drowned.death\": \"Drowned dies\",\n    \"subtitles.entity.drowned.hurt\": \"Drowned hurts\",\n    \"subtitles.entity.drowned.shoot\": \"Drowned throws Trident\",\n    \"subtitles.entity.drowned.step\": \"Drowned steps\",\n    \"subtitles.entity.drowned.swim\": \"Drowned swims\",\n    \"subtitles.entity.husk.converted_to_zombie\": \"Husk converted to Zombie\",\n    \"subtitles.entity.phantom.ambient\": \"Phantom screeches\",\n    \"subtitles.entity.phantom.bite\": \"Phantom bites\",\n    \"subtitles.entity.phantom.death\": \"Phantom dies\",\n    \"subtitles.entity.phantom.flap\": \"Phantom flaps\",\n    \"subtitles.entity.phantom.hurt\": \"Phantom hurts\",\n    \"subtitles.entity.phantom.swoop\": \"Phantom swoops\",\n    \"subtitles.entity.puffer_fish.blow_out\": \"Pufferfish deflates\",\n    \"subtitles.entity.puffer_fish.blow_up\": \"Pufferfish inflates\",\n    \"subtitles.entity.puffer_fish.death\": \"Pufferfish dies\",\n    \"subtitles.entity.puffer_fish.flop\": \"Pufferfish flops\",\n    \"subtitles.entity.puffer_fish.hurt\": \"Pufferfish hurts\",\n    \"subtitles.entity.puffer_fish.sting\": \"Pufferfish stings\",\n    \"subtitles.entity.salmon.death\": \"Salmon dies\",\n    \"subtitles.entity.salmon.flop\": \"Salmon flops\",\n    \"subtitles.entity.salmon.hurt\": \"Salmon hurts\",\n    \"subtitles.entity.skeleton_horse.swim\": \"Skeleton Horse swims\",\n    \"subtitles.entity.squid.squirt\": \"Squid shoots ink\",\n    \"subtitles.entity.turtle.ambient_land\": \"Turtle chirps\",\n    \"subtitles.entity.turtle.lay_egg\": \"Turtle lays egg\",\n    \"subtitles.entity.turtle.egg_hatch\": \"Turtle egg hatches\",\n    \"subtitles.entity.turtle.egg_crack\": \"Turtle egg cracks\",\n    \"subtitles.entity.turtle.egg_break\": \"Turtle egg breaks\",\n    \"subtitles.entity.turtle.hurt\": \"Turtle hurts\",\n    \"subtitles.entity.turtle.hurt_baby\": \"Turtle baby hurts\",\n    \"subtitles.entity.turtle.death\": \"Turtle dies\",\n    \"subtitles.entity.turtle.death_baby\": \"Turtle baby dies\",\n    \"subtitles.entity.turtle.swim\": \"Turtle swims\",\n    \"subtitles.entity.turtle.shamble\": \"Turtle shambles\",\n    \"subtitles.entity.turtle.shamble_baby\": \"Turtle baby shambles\",\n    \"subtitles.entity.zombie.converted_to_drowned\": \"Zombie converted to Drowned\",\n    \"subtitles.item.axe.strip\": \"Debarking log\",\n    \"subtitles.item.armor.equip_turtle\": \"Turtle shell thunks\",\n    \"subtitles.item.trident.hit\": \"Trident stabs\",\n    \"subtitles.item.trident.hit_ground\": \"Trident vibrates\",\n    \"subtitles.item.trident.return\": \"Trident returns\",\n    \"subtitles.item.trident.riptide\": \"Trident zooms\",\n    \"subtitles.item.trident.throw\": \"Trident clangs\",\n    \"subtitles.item.trident.thunder\": \"Trident thunder cracks\",\n    \"debug.reload_chunks.help\": \"F3 + A = Reload chunks\",\n    \"debug.show_hitboxes.help\": \"F3 + B = Show hitboxes\",\n    \"debug.clear_chat.help\": \"F3 + D = Clear chat\",\n    \"debug.cycle_renderdistance.help\": \"F3 + F = Cycle render distance (Shift to invert)\",\n    \"debug.chunk_boundaries.help\": \"F3 + G = Show chunk boundaries\",\n    \"debug.advanced_tooltips.help\": \"F3 + H = Advanced tooltips\",\n    \"debug.creative_spectator.help\": \"F3 + N = Cycle creative <-> spectator\",\n    \"debug.pause_focus.help\": \"F3 + P = Pause on lost focus\",\n    \"debug.help.help\": \"F3 + Q = Show this list\",\n    \"debug.reload_resourcepacks.help\": \"F3 + T = Reload resource packs\",\n    \"debug.copy_location.help\": \"F3 + C = Copy location as /tp command, hold F3 + C to crash the game\",\n    \"debug.inspect.help\": \"F3 + I = Copy entity or block data to clipboard\",\n    \"debug.copy_location.message\": \"Copied location to clipboard\",\n    \"debug.inspect.server.block\": \"Copied server-side block data to clipboard\",\n    \"debug.inspect.server.entity\": \"Copied server-side entity data to clipboard\",\n    \"debug.inspect.client.block\": \"Copied client-side block data to clipboard\",\n    \"debug.inspect.client.entity\": \"Copied client-side entity data to clipboard\",\n    \"debug.crash.message\": \"F3 + C is held down. This will crash the game unless released.\",\n    \"debug.crash.warning\": \"Crashing in %s...\",\n    \"advancements.adventure.very_very_frightening.title\": \"Very Very Frightening\",\n    \"advancements.adventure.very_very_frightening.description\": \"Strike a Villager with lightning\",\n    \"advancements.adventure.throw_trident.title\": \"A Throwaway Joke\",\n    \"advancements.adventure.throw_trident.description\": \"Throw a trident at something.\\nNote: Throwing away your only weapon is not a good idea.\",\n    \"advancements.husbandry.fishy_business.title\": \"Fishy Business\",\n    \"advancements.husbandry.fishy_business.description\": \"Catch a fish\",\n    \"advancements.husbandry.tactical_fishing.title\": \"Tactical Fishing\",\n    \"advancements.husbandry.tactical_fishing.description\": \"Catch a fish... without a fishing rod!\",\n    \"team.visibility.always\": \"Always\",\n    \"team.visibility.never\": \"Never\",\n    \"team.visibility.hideForOtherTeams\": \"Hide for other teams\",\n    \"team.visibility.hideForOwnTeam\": \"Hide for own team\",\n    \"team.collision.always\": \"Always\",\n    \"team.collision.never\": \"Never\",\n    \"team.collision.pushOtherTeams\": \"Push other teams\",\n    \"team.collision.pushOwnTeam\": \"Push own team\",\n    \"argument.entity.selector.nearestPlayer\": \"Nearest player\",\n    \"argument.entity.selector.randomPlayer\": \"Random player\",\n    \"argument.entity.selector.allPlayers\": \"All players\",\n    \"argument.entity.selector.allEntities\": \"All entities\",\n    \"argument.entity.selector.self\": \"Current entity\",\n    \"argument.entity.options.name.description\": \"Entity name\",\n    \"argument.entity.options.distance.description\": \"Distance to entity\",\n    \"argument.entity.options.level.description\": \"Experience level\",\n    \"argument.entity.options.x.description\": \"x position\",\n    \"argument.entity.options.y.description\": \"y position\",\n    \"argument.entity.options.z.description\": \"z position\",\n    \"argument.entity.options.dx.description\": \"Entities between x and x + dx\",\n    \"argument.entity.options.dy.description\": \"Entities between y and y + dy\",\n    \"argument.entity.options.dz.description\": \"Entities between z and z + dz\",\n    \"argument.entity.options.x_rotation.description\": \"Entity's x rotation\",\n    \"argument.entity.options.y_rotation.description\": \"Entity's y rotation\",\n    \"argument.entity.options.limit.description\": \"Maximum number of entities to return\",\n    \"argument.entity.options.sort.description\": \"Sort the entities\",\n    \"argument.entity.options.gamemode.description\": \"Players with gamemode\",\n    \"argument.entity.options.team.description\": \"Entities on team\",\n    \"argument.entity.options.type.description\": \"Entities of type\",\n    \"argument.entity.options.tag.description\": \"Entities with tag\",\n    \"argument.entity.options.nbt.description\": \"Entities with NBT\",\n    \"argument.entity.options.scores.description\": \"Entities with scores\",\n    \"argument.entity.options.advancements.description\": \"Players with advancements\",\n    \"command.failed\": \"An unexpected error occurred trying to execute that command\",\n    \"command.context.here\": \"<--[HERE]\",\n    \"commands.advancement.grant.one.to.one.success\": \"Granted the advancement %s to %s\",\n    \"commands.advancement.grant.one.to.one.failure\": \"Couldn't grant advancement %s to %s as they already have it\",\n    \"commands.advancement.grant.one.to.many.success\": \"Granted the advancement %s to %s players\",\n    \"commands.advancement.grant.one.to.many.failure\": \"Couldn't grant advancement %s to %s players as they already have it\",\n    \"commands.advancement.grant.many.to.one.success\": \"Granted %s advancements to %s\",\n    \"commands.advancement.grant.many.to.one.failure\": \"Couldn't grant %s advancements to %s as they already have them\",\n    \"commands.advancement.grant.many.to.many.success\": \"Granted %s advancements to %s players\",\n    \"commands.advancement.grant.many.to.many.failure\": \"Couldn't grant %s advancements to %s players as they already have them\",\n    \"commands.advancement.grant.criterion.to.one.success\": \"Granted criterion '%s' of advancement %s to %s\",\n    \"commands.advancement.grant.criterion.to.one.failure\": \"Couldn't grant criterion '%s' of advancement %s to %s as they already have it\",\n    \"commands.advancement.grant.criterion.to.many.success\": \"Granted criterion '%s' of advancement %s to %s players\",\n    \"commands.advancement.grant.criterion.to.many.failure\": \"Couldn't grant criterion '%s' of advancement %s to %s players as they already have it\",\n    \"commands.advancement.revoke.one.to.one.success\": \"Revoked the advancement %s from %s\",\n    \"commands.advancement.revoke.one.to.one.failure\": \"Couldn't revoke advancement %s from %s as they don't have it\",\n    \"commands.advancement.revoke.one.to.many.success\": \"Revoked the advancement %s from %s players\",\n    \"commands.advancement.revoke.one.to.many.failure\": \"Couldn't revoke advancement %s from %s players as they don't have it\",\n    \"commands.advancement.revoke.many.to.one.success\": \"Revoked %s advancements from %s\",\n    \"commands.advancement.revoke.many.to.one.failure\": \"Couldn't revoke %s advancements from %s as they don't have them\",\n    \"commands.advancement.revoke.many.to.many.success\": \"Revoked %s advancements from %s players\",\n    \"commands.advancement.revoke.many.to.many.failure\": \"Couldn't revoke %s advancements from %s players as they don't have them\",\n    \"commands.advancement.revoke.criterion.to.one.success\": \"Revoked criterion '%s' of advancement %s from %s\",\n    \"commands.advancement.revoke.criterion.to.one.failure\": \"Couldn't revoke criterion '%s' of advancement %s from %s as they don't have it\",\n    \"commands.advancement.revoke.criterion.to.many.success\": \"Revoked criterion '%s' of advancement %s from %s players\",\n    \"commands.advancement.revoke.criterion.to.many.failure\": \"Couldn't revoke criterion '%s' of advancement %s from %s players as they don't have it\",\n    \"commands.clear.success.single\": \"Removed %s items from player %s\",\n    \"commands.clear.success.multiple\": \"Removed %s items from %s players\",\n    \"commands.clear.test.single\": \"Found %s matching items on player %s\",\n    \"commands.clear.test.multiple\": \"Found %s matching items on %s players\",\n    \"commands.debug.stopped\": \"Stopped debug profiling after %s seconds and %s ticks (%s ticks per second)\",\n    \"commands.difficulty.query\": \"The difficulty is %s\",\n    \"commands.effect.give.success.single\": \"Applied effect %s to %s\",\n    \"commands.effect.give.success.multiple\": \"Applied effect %s to %s targets\",\n    \"commands.effect.clear.everything.success.single\": \"Removed every effect from %s\",\n    \"commands.effect.clear.everything.success.multiple\": \"Removed every effect from %s targets\",\n    \"commands.effect.clear.specific.success.single\": \"Removed effect %s from %s\",\n    \"commands.effect.clear.specific.success.multiple\": \"Removed effect %s from %s targets\",\n    \"commands.enchant.success.single\": \"Applied enchantment %s to %s's item\",\n    \"commands.enchant.success.multiple\": \"Applied enchantment %s to %s entities\",\n    \"commands.experience.add.points.success.single\": \"Gave %s experience points to %s\",\n    \"commands.experience.add.points.success.multiple\": \"Gave %s experience points to %s players\",\n    \"commands.experience.add.levels.success.single\": \"Gave %s experience levels to %s\",\n    \"commands.experience.add.levels.success.multiple\": \"Gave %s experience levels to %s players\",\n    \"commands.experience.set.points.success.single\": \"Set %s experience points on %s\",\n    \"commands.experience.set.points.success.multiple\": \"Set %s experience points on %s players\",\n    \"commands.experience.set.levels.success.single\": \"Set %s experience levels on %s\",\n    \"commands.experience.set.levels.success.multiple\": \"Set %s experience levels on %s players\",\n    \"commands.experience.query.points\": \"%s has %s experience points\",\n    \"commands.experience.query.levels\": \"%s has %s experience levels\",\n    \"commands.function.success.single\": \"Executed %s commands from function '%s'\",\n    \"commands.function.success.multiple\": \"Executed %s commands from %s functions\",\n    \"commands.give.success.single\": \"Gave %s %s to %s\",\n    \"commands.give.success.multiple\": \"Gave %s %s to %s players\",\n    \"commands.playsound.success.single\": \"Played sound %s to %s\",\n    \"commands.playsound.success.multiple\": \"Played sound %s to %s players\",\n    \"commands.publish.success\": \"Multiplayer game is now hosted on port %s\",\n    \"commands.list.players\": \"There are %s of a max %s players online: %s\",\n    \"commands.kill.success.multiple\": \"Killed %s entities\",\n    \"commands.pardon.success\": \"Unbanned %s\",\n    \"commands.gamerule.query\": \"Gamerule %s is currently set to: %s\",\n    \"commands.gamerule.set\": \"Gamerule %s is now set to: %s\",\n    \"commands.save.saving\": \"Saving the game (this may take a moment!)\",\n    \"commands.banlist.none\": \"There are no bans\",\n    \"commands.banlist.list\": \"There are %s bans:\",\n    \"commands.banlist.entry\": \"%s was banned by %s: %s\",\n    \"commands.bossbar.create.success\": \"Created custom bossbar %s\",\n    \"commands.bossbar.remove.success\": \"Removed custom bossbar %s\",\n    \"commands.bossbar.list.bars.none\": \"There are no custom bossbars active\",\n    \"commands.bossbar.list.bars.some\": \"There are %s custom bossbars active: %s\",\n    \"commands.bossbar.set.players.success.none\": \"Custom bossbar %s no longer has any players\",\n    \"commands.bossbar.set.players.success.some\": \"Custom bossbar %s now has %s players: %s\",\n    \"commands.bossbar.set.name.success\": \"Custom bossbar %s has been renamed\",\n    \"commands.bossbar.set.color.success\": \"Custom bossbar %s has changed color\",\n    \"commands.bossbar.set.style.success\": \"Custom bossbar %s has changed style\",\n    \"commands.bossbar.set.value.success\": \"Custom bossbar %s has changed value to %s\",\n    \"commands.bossbar.set.max.success\": \"Custom bossbar %s has changed maximum to %s\",\n    \"commands.bossbar.set.visible.success.visible\": \"Custom bossbar %s is now visible\",\n    \"commands.bossbar.set.visible.success.hidden\": \"Custom bossbar %s is now hidden\",\n    \"commands.bossbar.get.value\": \"Custom bossbar %s has a value of %s\",\n    \"commands.bossbar.get.max\": \"Custom bossbar %s has a maximum of %s\",\n    \"commands.bossbar.get.visible.visible\": \"Custom bossbar %s is currently shown\",\n    \"commands.bossbar.get.visible.hidden\": \"Custom bossbar %s is currently hidden\",\n    \"commands.bossbar.get.players.none\": \"Custom bossbar %s has no players currently online\",\n    \"commands.bossbar.get.players.some\": \"Custom bossbar %s has %s players currently online: %s\",\n    \"commands.recipe.give.success.single\": \"Unlocked %s recipes for %s\",\n    \"commands.recipe.give.success.multiple\": \"Unlocked %s recipes for %s players\",\n    \"commands.recipe.take.success.single\": \"Took %s recipes from %s\",\n    \"commands.recipe.take.success.multiple\": \"Took %s recipes from %s players\",\n    \"commands.whitelist.none\": \"There are no whitelisted players\",\n    \"commands.weather.set.clear\": \"Set the weather to clear\",\n    \"commands.weather.set.rain\": \"Set the weather to rain\",\n    \"commands.weather.set.thunder\": \"Set the weather to rain & thunder\",\n    \"commands.spawnpoint.success.single\": \"Set spawn point to %s, %s, %s for %s\",\n    \"commands.spawnpoint.success.multiple\": \"Set spawn point to %s, %s, %s for %s players\",\n    \"commands.stopsound.success.source.sound\": \"Stopped sound '%s' on source '%s'\",\n    \"commands.stopsound.success.source.any\": \"Stopped all '%s' sounds\",\n    \"commands.stopsound.success.sourceless.sound\": \"Stopped sound '%s'\",\n    \"commands.stopsound.success.sourceless.any\": \"Stopped all sounds\",\n    \"commands.spreadplayers.success.entities\": \"Spread %s players around %s, %s with an average distance of %s blocks apart\",\n    \"commands.setblock.success\": \"Changed the block at %s, %s, %s\",\n    \"commands.banip.info\": \"This ban affects %s players: %s\",\n    \"commands.pardonip.success\": \"Unbanned IP %s\",\n    \"commands.teleport.success.entity.multiple\": \"Teleported %s entities to %s\",\n    \"commands.teleport.success.location.multiple\": \"Teleported %s entities to %s, %s, %s\",\n    \"commands.title.cleared.single\": \"Cleared titles for %s\",\n    \"commands.title.cleared.multiple\": \"Cleared titles for %s players\",\n    \"commands.title.reset.single\": \"Reset title options for %s\",\n    \"commands.title.reset.multiple\": \"Reset title options for %s players\",\n    \"commands.title.show.title.single\": \"Showing new title for %s\",\n    \"commands.title.show.title.multiple\": \"Showing new title for %s players\",\n    \"commands.title.show.subtitle.single\": \"Showing new subtitle for %s\",\n    \"commands.title.show.subtitle.multiple\": \"Showing new subtitle for %s players\",\n    \"commands.title.show.actionbar.single\": \"Showing new actionbar title for %s\",\n    \"commands.title.show.actionbar.multiple\": \"Showing new actionbar title for %s players\",\n    \"commands.title.times.single\": \"Changed title display times for %s\",\n    \"commands.title.times.multiple\": \"Changed title display times for %s players\",\n    \"commands.worldborder.set.grow\": \"Growing the world border to %s blocks wide over %s seconds\",\n    \"commands.worldborder.set.shrink\": \"Shrinking the world border to %s blocks wide over %s seconds\",\n    \"commands.worldborder.set.immediate\": \"Set the world border to %s blocks wide\",\n    \"commands.worldborder.get\": \"The world border is currently %s blocks wide\",\n    \"commands.replaceitem.block.success\": \"Replaced a slot at %s, %s, %s with %s\",\n    \"commands.replaceitem.entity.success.single\": \"Replaced a slot on %s with %s\",\n    \"commands.replaceitem.entity.success.multiple\": \"Replaced a slot on %s entities with %s\",\n    \"commands.tag.add.success.single\": \"Added tag '%s' to %s\",\n    \"commands.tag.add.success.multiple\": \"Added tag '%s' to %s entities\",\n    \"commands.tag.remove.success.single\": \"Removed tag '%s' from %s\",\n    \"commands.tag.remove.success.multiple\": \"Removed tag '%s' from %s entities\",\n    \"commands.tag.list.single.empty\": \"%s has no tags\",\n    \"commands.tag.list.single.success\": \"%s has %s tags: %s\",\n    \"commands.tag.list.multiple.empty\": \"There are no tags on the %s entities\",\n    \"commands.tag.list.multiple.success\": \"The %s entities have %s total tags: %s\",\n    \"commands.team.list.members.empty\": \"There are no members on team %s\",\n    \"commands.team.list.members.success\": \"Team %s has %s members: %s\",\n    \"commands.team.list.teams.empty\": \"There are no teams\",\n    \"commands.team.list.teams.success\": \"There are %s teams: %s\",\n    \"commands.team.add.success\": \"Created team %s\",\n    \"commands.team.empty.success\": \"Removed %s members from team %s\",\n    \"commands.team.option.color.success\": \"Updated the color for team %s to %s\",\n    \"commands.team.option.name.success\": \"Updated team %s name\",\n    \"commands.team.option.friendlyfire.enabled\": \"Enabled friendly fire for team %s\",\n    \"commands.team.option.friendlyfire.disabled\": \"Disabled friendly fire for team %s\",\n    \"commands.team.option.seeFriendlyInvisibles.enabled\": \"Team %s can now see invisible teammates\",\n    \"commands.team.option.seeFriendlyInvisibles.disabled\": \"Team %s can no longer see invisible teammates\",\n    \"commands.team.option.nametagVisibility.success\": \"Nametag visibility for team %s is now \\\"%s\\\"\",\n    \"commands.team.option.deathMessageVisibility.success\": \"Death message visibility for team %s is now \\\"%s\\\"\",\n    \"commands.team.option.collisionRule.success\": \"Collision rule for team %s is now \\\"%s\\\"\",\n    \"commands.team.option.prefix.success\": \"Team prefix set to %s\",\n    \"commands.team.option.suffix.success\": \"Team suffix set to %s\",\n    \"commands.team.join.success.single\": \"Added %s to team %s\",\n    \"commands.team.join.success.multiple\": \"Added %s members to team %s\",\n    \"commands.team.leave.success.single\": \"Removed %s from any team\",\n    \"commands.team.leave.success.multiple\": \"Removed %s members from any team\",\n    \"commands.trigger.simple.success\": \"Triggered %s\",\n    \"commands.trigger.add.success\": \"Triggered %s (added %s to value)\",\n    \"commands.trigger.set.success\": \"Triggered %s (set value to %s)\",\n    \"commands.scoreboard.objectives.list.success\": \"There are %s objectives: %s\",\n    \"commands.scoreboard.objectives.display.cleared\": \"Cleared any objectives in display slot %s\",\n    \"commands.scoreboard.objectives.display.set\": \"Set display slot %s to show objective %s\",\n    \"commands.scoreboard.objectives.modify.displayname\": \"Changed objective %s display name to %s\",\n    \"commands.scoreboard.objectives.modify.rendertype\": \"Changed objective %s render type\",\n    \"commands.scoreboard.players.list.success\": \"There are %s tracked entities: %s\",\n    \"commands.scoreboard.players.list.entity.empty\": \"%s has no scores to show\",\n    \"commands.scoreboard.players.list.entity.success\": \"%s has %s scores:\",\n    \"commands.scoreboard.players.list.entity.entry\": \"%s: %s\",\n    \"commands.scoreboard.players.set.success.single\": \"Set %s for %s to %s\",\n    \"commands.scoreboard.players.set.success.multiple\": \"Set %s for %s entities to %s\",\n    \"commands.scoreboard.players.add.success.single\": \"Added %s to %s for %s (now %s)\",\n    \"commands.scoreboard.players.add.success.multiple\": \"Added %s to %s for %s entities\",\n    \"commands.scoreboard.players.remove.success.single\": \"Removed %s from %s for %s (now %s)\",\n    \"commands.scoreboard.players.remove.success.multiple\": \"Removed %s from %s for %s entities\",\n    \"commands.scoreboard.players.reset.all.single\": \"Reset all scores for %s\",\n    \"commands.scoreboard.players.reset.all.multiple\": \"Reset all scores for %s entities\",\n    \"commands.scoreboard.players.reset.specific.single\": \"Reset %s for %s\",\n    \"commands.scoreboard.players.reset.specific.multiple\": \"Reset %s for %s entities\",\n    \"commands.scoreboard.players.enable.success.multiple\": \"Enabled trigger %s for %s entities\",\n    \"commands.scoreboard.players.operation.success.single\": \"Set %s for %s to %s\",\n    \"commands.scoreboard.players.operation.success.multiple\": \"Updated %s for %s entities\",\n    \"commands.scoreboard.players.get.success\": \"%s has %s %s\",\n    \"commands.data.entity.modified\": \"Modified entity data of %s\",\n    \"commands.data.entity.query\": \"%s has the following entity data: %s\",\n    \"commands.data.entity.get\": \"%s on %s after scale factor of %s is %s\",\n    \"commands.data.block.modified\": \"Modified block data of %s, %s, %s\",\n    \"commands.data.block.query\": \"%s, %s, %s has the following block data: %s\",\n    \"commands.data.block.get\": \"%s on block %s, %s, %s after scale factor of %s is %s\",\n    \"commands.datapack.list.enabled.success\": \"There are %s data packs enabled: %s\",\n    \"commands.datapack.list.enabled.none\": \"There are no data packs enabled\",\n    \"commands.datapack.list.available.success\": \"There are %s data packs available: %s\",\n    \"commands.datapack.list.available.none\": \"There are no more data packs available\",\n    \"commands.datapack.enable.success\": \"Enabled data pack %s\",\n    \"commands.datapack.disable.success\": \"Disabled data pack %s\",\n    \"argument.range.empty\": \"Expected value or range of values\",\n    \"argument.range.ints\": \"Only whole numbers allowed, not decimals\",\n    \"argument.range.swapped\": \"Min cannot be bigger than max\",\n    \"permissions.requires.player\": \"A player is required to run this command here\",\n    \"permissions.requires.entity\": \"An entity is required to run this command here\",\n    \"argument.entity.toomany\": \"Only one entity is allowed, but the provided selector allows more than one\",\n    \"argument.player.toomany\": \"Only one player is allowed, but the provided selector allows more than one\",\n    \"argument.player.entities\": \"Only players may be affected by this command, but the provided selector includes entities\",\n    \"argument.entity.notfound.entity\": \"No entity was found\",\n    \"argument.entity.notfound.player\": \"No player was found\",\n    \"argument.player.unknown\": \"That player does not exist\",\n    \"arguments.nbtpath.node.invalid\": \"Invalid NBT path, expected element name or index\",\n    \"arguments.operation.invalid\": \"Invalid operation\",\n    \"arguments.operation.div0\": \"Cannot divide by zero\",\n    \"argument.scoreHolder.empty\": \"No relevant score holders could be found\",\n    \"argument.block.tag.disallowed\": \"Tags aren't allowed here, only actual blocks\",\n    \"argument.block.property.unclosed\": \"Expected closing ] for block state properties\",\n    \"argument.pos.unloaded\": \"That position is not loaded\",\n    \"argument.pos.outofworld\": \"That position is out of this world!\",\n    \"argument.rotation.incomplete\": \"Incomplete (expected 2 coordinates)\",\n    \"arguments.swizzle.invalid\": \"Invalid swizzle, expected combination of 'x', 'y' and 'z'\",\n    \"argument.vec2.incomplete\": \"Incomplete (expected 2 coordinates)\",\n    \"argument.pos.incomplete\": \"Incomplete (expected 3 coordinates)\",\n    \"argument.pos.mixed\": \"Cannot mix world & local coordinates (everything must either use ^ or not)\",\n    \"argument.pos.missing.double\": \"Expected a coordinate\",\n    \"argument.pos.missing.int\": \"Expected a block position\",\n    \"argument.item.tag.disallowed\": \"Tags aren't allowed here, only actual items\",\n    \"argument.entity.invalid\": \"Invalid name or UUID\",\n    \"argument.entity.selector.missing\": \"Missing selector type\",\n    \"argument.entity.selector.not_allowed\": \"Selector not allowed\",\n    \"argument.entity.options.unterminated\": \"Expected end of options\",\n    \"argument.entity.options.distance.negative\": \"Distance cannot be negative\",\n    \"argument.entity.options.level.negative\": \"Level shouldn't be negative\",\n    \"argument.entity.options.limit.toosmall\": \"Limit must be at least 1\",\n    \"argument.nbt.trailing\": \"Unexpected trailing data\",\n    \"argument.nbt.expected.key\": \"Expected key\",\n    \"argument.nbt.expected.value\": \"Expected value\",\n    \"argument.id.invalid\": \"Invalid ID\",\n    \"commands.banip.failed\": \"Nothing changed. That IP is already banned\",\n    \"commands.bossbar.set.players.unchanged\": \"Nothing changed. Those players are already on the bossbar with nobody to add or remove\",\n    \"commands.bossbar.set.name.unchanged\": \"Nothing changed. That's already the name of this bossbar\",\n    \"commands.bossbar.set.color.unchanged\": \"Nothing changed. That's already the color of this bossbar\",\n    \"commands.bossbar.set.style.unchanged\": \"Nothing changed. That's already the style of this bossbar\",\n    \"commands.bossbar.set.value.unchanged\": \"Nothing changed. That's already the value of this bossbar\",\n    \"commands.bossbar.set.max.unchanged\": \"Nothing changed. That's already the max of this bossbar\",\n    \"commands.bossbar.set.visibility.unchanged.hidden\": \"Nothing changed. The bossbar is already hidden\",\n    \"commands.bossbar.set.visibility.unchanged.visible\": \"Nothing changed. The bossbar is already visible\",\n    \"commands.clone.overlap\": \"The source and destination areas cannot overlap\",\n    \"commands.debug.notRunning\": \"The debug profiler hasn't started\",\n    \"commands.debug.alreadyRunning\": \"The debug profiler is already started\",\n    \"commands.effect.give.failed\": \"Unable to apply this effect (target is either immune to effects, or has something stronger)\",\n    \"commands.effect.clear.everything.failed\": \"Target has no effects to remove\",\n    \"commands.effect.clear.specific.failed\": \"Target doesn't have the requested effect\",\n    \"commands.enchant.failed\": \"Nothing changed. Targets either have no item in their hands or the enchantment could not be applied\",\n    \"commands.experience.set.points.invalid\": \"Cannot set experience points above the maximum points for the player's current level\",\n    \"commands.help.failed\": \"Unknown command or insufficient permissions\",\n    \"commands.locate.failed\": \"Could not find that structure nearby\",\n    \"commands.pardon.failed\": \"Nothing changed. The player isn't banned\",\n    \"commands.pardonip.invalid\": \"Invalid IP address\",\n    \"commands.pardonip.failed\": \"Nothing changed. That IP isn't banned\",\n    \"commands.particle.failed\": \"The particle was not visible for anybody\",\n    \"commands.playsound.failed\": \"The sound is too far away to be heard\",\n    \"commands.recipe.give.failed\": \"No new recipes were learned\",\n    \"commands.recipe.take.failed\": \"No recipes could be forgotten\",\n    \"commands.replaceitem.block.failed\": \"The target block is not a container\",\n    \"commands.replaceitem.slot.inapplicable\": \"The target does not have slot %s\",\n    \"commands.replaceitem.entity.failed\": \"Could not put %s in slot %s\",\n    \"commands.scoreboard.objectives.add.duplicate\": \"An objective already exists by that name\",\n    \"commands.scoreboard.objectives.display.alreadyEmpty\": \"Nothing changed. That display slot is already empty\",\n    \"commands.scoreboard.objectives.display.alreadySet\": \"Nothing changed. That display slot is already showing that objective\",\n    \"commands.scoreboard.players.enable.failed\": \"Nothing changed. That trigger is already enabled\",\n    \"commands.scoreboard.players.enable.invalid\": \"Enable only works on trigger-objectives\",\n    \"commands.tag.add.failed\": \"Target either already has the tag or has too many tags\",\n    \"commands.tag.remove.failed\": \"Target does not have this tag\",\n    \"commands.team.add.duplicate\": \"A team already exists by that name\",\n    \"commands.team.empty.unchanged\": \"Nothing changed. That team is already empty\",\n    \"commands.team.option.color.unchanged\": \"Nothing changed. That team already has that color\",\n    \"commands.team.option.name.unchanged\": \"Nothing changed. That team already has that name\",\n    \"commands.team.option.friendlyfire.alreadyEnabled\": \"Nothing changed. Friendly fire is already enabled for that team\",\n    \"commands.team.option.friendlyfire.alreadyDisabled\": \"Nothing changed. Friendly fire is already disabled for that team\",\n    \"commands.team.option.seeFriendlyInvisibles.alreadyEnabled\": \"Nothing changed. That team can already see invisible teammates\",\n    \"commands.team.option.seeFriendlyInvisibles.alreadyDisabled\": \"Nothing changed. That team already can't see invisible teammates\",\n    \"commands.team.option.nametagVisibility.unchanged\": \"Nothing changed. Nametag visibility is already that value\",\n    \"commands.team.option.deathMessageVisibility.unchanged\": \"Nothing changed. Death message visibility is already that value\",\n    \"commands.team.option.collisionRule.unchanged\": \"Nothing changed. Collision rule is already that value\",\n    \"commands.trigger.failed.unprimed\": \"You cannot trigger this objective yet\",\n    \"commands.trigger.failed.invalid\": \"You can only trigger objectives that are 'trigger' type\",\n    \"commands.whitelist.alreadyOn\": \"Whitelist is already turned on\",\n    \"commands.whitelist.alreadyOff\": \"Whitelist is already turned off\",\n    \"commands.worldborder.center.failed\": \"Nothing changed. The world border is already centered there\",\n    \"commands.worldborder.set.failed.nochange\": \"Nothing changed. The world border is already that size\",\n    \"commands.worldborder.set.failed.small.\": \"World border cannot be smaller than 1 block wide\",\n    \"commands.worldborder.set.failed.big.\": \"World border cannot be bigger than 60,000,000 blocks wide\",\n    \"commands.worldborder.warning.time.failed\": \"Nothing changed. The world border warning is already that amount of time\",\n    \"commands.worldborder.warning.distance.failed\": \"Nothing changed. The world border warning is already that distance\",\n    \"commands.worldborder.damage.buffer.failed\": \"Nothing changed. The world border damage buffer is already that distance\",\n    \"commands.worldborder.damage.amount.failed\": \"Nothing changed. The world border damage is already that amount\",\n    \"commands.data.block.invalid\": \"The target block is not a block entity\",\n    \"commands.data.merge.failed\": \"Nothing changed, the specified properties already have these values\",\n    \"commands.data.entity.invalid\": \"Unable to modify player data\",\n    \"argument.color.invalid\": \"Unknown color '%s'\",\n    \"argument.component.invalid\": \"Invalid chat component: %s\",\n    \"argument.anchor.invalid\": \"Invalid entity anchor position %s\",\n    \"enchantment.unknown\": \"Unknown enchantment: %s\",\n    \"effect.effectNotFound\": \"Unknown effect: %s\",\n    \"argument.nbt.invalid\": \"Invalid NBT: %s\",\n    \"arguments.nbtpath.child.invalid\": \"Can't access child '%s', either doesn't exist or parent isn't a compound\",\n    \"arguments.nbtpath.element.invalid\": \"Can't access element %s, either doesn't exist or parent isn't a list\",\n    \"arguments.objective.notFound\": \"Unknown scoreboard objective '%s'\",\n    \"arguments.objective.readonly\": \"Scoreboard objective '%s' is read-only\",\n    \"commands.scoreboard.objectives.add.longName\": \"Objective names cannot be longer than %s characters\",\n    \"argument.criteria.invalid\": \"Unknown criteria '%s'\",\n    \"particle.notFound\": \"Unknown particle: %s\",\n    \"argument.id.unknown\": \"Unknown ID: %s\",\n    \"advancement.advancementNotFound\": \"Unknown advancement: %s\",\n    \"recipe.notFound\": \"Unknown recipe: %s\",\n    \"entity.notFound\": \"Unknown entity: %s\",\n    \"argument.scoreboardDisplaySlot.invalid\": \"Unknown display slot '%s'\",\n    \"slot.unknown\": \"Unknown slot '%s'\",\n    \"team.notFound\": \"Unknown team '%s'\",\n    \"arguments.block.tag.unknown\": \"Unknown block tag '%s'\",\n    \"argument.block.id.invalid\": \"Unknown block type '%s'\",\n    \"argument.block.property.unknown\": \"Block %s does not have property '%s'\",\n    \"argument.block.property.duplicate\": \"Property '%s' can only be set once for block %s\",\n    \"argument.block.property.invalid\": \"Block %s does not accept '%s' for %s property\",\n    \"argument.block.property.novalue\": \"Expected value for property '%s' on block %s\",\n    \"arguments.function.tag.unknown\": \"Unknown function tag '%s'\",\n    \"arguments.function.unknown\": \"Unknown function %s\",\n    \"arguments.item.overstacked\": \"%s can only stack up to %s\",\n    \"argument.item.id.invalid\": \"Unknown item '%s'\",\n    \"arguments.item.tag.unknown\": \"Unknown item tag '%s'\",\n    \"argument.entity.selector.unknown\": \"Unknown selector type '%s'\",\n    \"argument.entity.options.valueless\": \"Expected value for option '%s'\",\n    \"argument.entity.options.unknown\": \"Unknown option '%s'\",\n    \"argument.entity.options.inapplicable\": \"Option '%s' isn't applicable here\",\n    \"argument.entity.options.sort.irreversible\": \"Invalid or unknown sort type '%s'\",\n    \"argument.entity.options.mode.invalid\": \"Invalid or unknown game mode '%s'\",\n    \"argument.entity.options.type.invalid\": \"Invalid or unknown entity type '%s'\",\n    \"argument.nbt.list.mixed\": \"Can't insert %s into list of %s\",\n    \"argument.nbt.array.mixed\": \"Can't insert %s into %s\",\n    \"argument.nbt.array.invalid\": \"Invalid array type '%s'\",\n    \"commands.bossbar.create.failed\": \"A bossbar already exists with the ID '%s'\",\n    \"commands.bossbar.unknown\": \"No bossbar exists with the ID '%s'\",\n    \"clear.failed.single\": \"No items were found on player %s\",\n    \"clear.failed.multiple\": \"No items were found on %s players\",\n    \"commands.clone.toobig\": \"Too many blocks in the specified area (maximum %s, specified %s)\",\n    \"commands.datapack.unknown\": \"Unknown data pack '%s'\",\n    \"commands.datapack.enable.failed\": \"Pack '%s' is already enabled!\",\n    \"commands.datapack.disable.failed\": \"Pack '%s' is not enabled!\",\n    \"commands.difficulty.failure\": \"The difficulty did not change; it is already set to %s\",\n    \"commands.enchant.failed.entity\": \"%s is not a valid entity for this command\",\n    \"commands.enchant.failed.itemless\": \"%s is not holding any item\",\n    \"commands.enchant.failed.incompatible\": \"%s cannot support that enchantment\",\n    \"commands.enchant.failed.level\": \"%s is higher than the maximum level of %s supported by that enchantment\",\n    \"commands.execute.blocks.toobig\": \"Too many blocks in the specified area (maximum %s, specified %s)\",\n    \"commands.execute.conditional.pass\": \"Test passed\",\n    \"commands.execute.conditional.pass_count\": \"Test passed, count: %s\",\n    \"commands.execute.conditional.fail\": \"Test failed\",\n    \"commands.execute.conditional.fail_count\": \"Test failed, count: %s\",\n    \"commands.fill.toobig\": \"Too many blocks in the specified area (maximum %s, specified %s)\",\n    \"commands.publish.alreadyPublished\": \"Multiplayer game is already hosted on port %s\",\n    \"commands.scoreboard.players.get.null\": \"Can't get value of %s for %s; none is set\",\n    \"commands.spreadplayers.failed.teams\": \"Could not spread %s teams around %s, %s (too many entities for space - try using spread of at most %s)\",\n    \"commands.spreadplayers.failed.entities\": \"Could not spread %s entities around %s, %s (too many entities for space - try using spread of at most %s)\",\n    \"commands.team.add.longName\": \"Team names cannot be longer than %s characters\",\n    \"commands.data.get.invalid\": \"Can't get %s; only numeric tags are allowed\",\n    \"commands.data.get.unknown\": \"Can't get %s; tag doesn't exist\",\n    \"argument.double.low\": \"Double must not be less than %s, found %s\",\n    \"argument.double.big\": \"Double must not be more than %s, found %s\",\n    \"argument.float.low\": \"Float must not be less than %s, found %s\",\n    \"argument.float.big\": \"Float must not be more than %s, found %s\",\n    \"argument.integer.low\": \"Integer must not be less than %s, found %s\",\n    \"argument.integer.big\": \"Integer must not be more than %s, found %s\",\n    \"argument.literal.incorrect\": \"Expected literal %s\",\n    \"parsing.quote.expected.start\": \"Expected quote to start a string\",\n    \"parsing.quote.expected.end\": \"Unclosed quoted string\",\n    \"parsing.quote.escape\": \"Invalid escape sequence '\\\\%s' in quoted string\",\n    \"parsing.bool.invalid\": \"Invalid boolean, expected 'true' or 'false' but found '%s'\",\n    \"parsing.int.invalid\": \"Invalid integer '%s'\",\n    \"parsing.int.expected\": \"Expected integer\",\n    \"command.exception\": \"Could not parse command: %s\",\n    \"parsing.double.invalid\": \"Invalid double '%s'\",\n    \"parsing.double.expected\": \"Expected double\",\n    \"parsing.float.invalid\": \"Invalid float '%s'\",\n    \"parsing.float.expected\": \"Expected float\",\n    \"parsing.bool.expected\": \"Expected boolean\",\n    \"parsing.expected\": \"Expected '%s'\",\n    \"command.unknown.command\": \"Unknown command\",\n    \"command.unknown.argument\": \"Incorrect argument for command\",\n    \"command.expected.separator\": \"Expected whitespace to end one argument, but found trailing data\",\n    \"biome.minecraft.beach\": \"Beach\",\n    \"biome.minecraft.birch_forest\": \"Birch Forest\",\n    \"biome.minecraft.birch_forest_hills\": \"Birch Forest Hills\",\n    \"biome.minecraft.snowy_beach\": \"Snowy Beach\",\n    \"biome.minecraft.cold_ocean\": \"Cold Ocean\",\n    \"biome.minecraft.deep_cold_ocean\": \"Deep Cold Ocean\",\n    \"biome.minecraft.deep_frozen_ocean\": \"Deep Frozen Ocean\",\n    \"biome.minecraft.deep_lukewarm_ocean\": \"Deep Lukewarm Ocean\",\n    \"biome.minecraft.deep_ocean\": \"Deep Ocean\",\n    \"biome.minecraft.deep_warm_ocean\": \"Deep Warm Ocean\",\n    \"biome.minecraft.desert\": \"Desert\",\n    \"biome.minecraft.desert_hills\": \"Desert Hills\",\n    \"biome.minecraft.mountains\": \"Mountains\",\n    \"biome.minecraft.wooded_mountains\": \"Wooded Mountains\",\n    \"biome.minecraft.forest\": \"Forest\",\n    \"biome.minecraft.wooded_hills\": \"Wooded Hills\",\n    \"biome.minecraft.frozen_ocean\": \"Frozen Ocean\",\n    \"biome.minecraft.frozen_river\": \"Frozen River\",\n    \"biome.minecraft.nether\": \"Nether\",\n    \"biome.minecraft.snowy_tundra\": \"Snowy Tundra\",\n    \"biome.minecraft.snowy_mountains\": \"Snowy Mountains\",\n    \"biome.minecraft.jungle_edge\": \"Jungle Edge\",\n    \"biome.minecraft.jungle_hills\": \"Jungle Hills\",\n    \"biome.minecraft.jungle\": \"Jungle\",\n    \"biome.minecraft.lukewarm_ocean\": \"Lukewarm Ocean\",\n    \"biome.minecraft.badlands_plateau\": \"Badlands Plateau\",\n    \"biome.minecraft.badlands\": \"Badlands\",\n    \"biome.minecraft.wooded_badlands_plateau\": \"Wooded Badlands Plateau\",\n    \"biome.minecraft.mushroom_fields\": \"Mushroom Fields\",\n    \"biome.minecraft.mushroom_field_shore\": \"Mushroom Field Shore\",\n    \"biome.minecraft.tall_birch_hills\": \"Tall Birch Hills\",\n    \"biome.minecraft.tall_birch_forest\": \"Tall Birch Forest\",\n    \"biome.minecraft.desert_lakes\": \"Desert Lakes\",\n    \"biome.minecraft.gravelly_mountains\": \"Gravelly Mountains\",\n    \"biome.minecraft.modified_gravelly_mountains\": \"Gravelly Mountains+\",\n    \"biome.minecraft.flower_forest\": \"Flower Forest\",\n    \"biome.minecraft.ice_spikes\": \"Ice Spikes\",\n    \"biome.minecraft.modified_jungle_edge\": \"Modified Jungle Edge\",\n    \"biome.minecraft.modified_jungle\": \"Modified Jungle\",\n    \"biome.minecraft.modified_badlands_plateau\": \"Modified Badlands Plateau\",\n    \"biome.minecraft.eroded_badlands\": \"Eroded Badlands\",\n    \"biome.minecraft.modified_wooded_badlands_plateau\": \"Modified Wooded Badlands Plateau\",\n    \"biome.minecraft.sunflower_plains\": \"Sunflower Plains\",\n    \"biome.minecraft.giant_spruce_taiga_hills\": \"Giant Spruce Taiga Hills\",\n    \"biome.minecraft.giant_spruce_taiga\": \"Giant Spruce Taiga\",\n    \"biome.minecraft.dark_forest_hills\": \"Dark Forest Hills\",\n    \"biome.minecraft.shattered_savanna\": \"Shattered Savanna\",\n    \"biome.minecraft.shattered_savanna_plateau\": \"Shattered Savanna Plateau\",\n    \"biome.minecraft.swamp_hills\": \"Swamp Hills\",\n    \"biome.minecraft.snowy_taiga_mountains\": \"Snowy Taiga Mountains\",\n    \"biome.minecraft.taiga_mountains\": \"Taiga Mountains\",\n    \"biome.minecraft.ocean\": \"Ocean\",\n    \"biome.minecraft.plains\": \"Plains\",\n    \"biome.minecraft.giant_tree_taiga_hills\": \"Giant Tree Taiga Hills\",\n    \"biome.minecraft.giant_tree_taiga\": \"Giant Tree Taiga\",\n    \"biome.minecraft.river\": \"River\",\n    \"biome.minecraft.dark_forest\": \"Dark Forest\",\n    \"biome.minecraft.savanna_plateau\": \"Savanna Plateau\",\n    \"biome.minecraft.savanna\": \"Savanna\",\n    \"biome.minecraft.end_barrens\": \"End Barrens\",\n    \"biome.minecraft.end_highlands\": \"End Highlands\",\n    \"biome.minecraft.small_end_islands\": \"Small End Islands\",\n    \"biome.minecraft.end_midlands\": \"End Midlands\",\n    \"biome.minecraft.the_end\": \"The End\",\n    \"biome.minecraft.mountain_edge\": \"Mountain Edge\",\n    \"biome.minecraft.stone_shore\": \"Stone Shore\",\n    \"biome.minecraft.swamp\": \"Swamp\",\n    \"biome.minecraft.snowy_taiga\": \"Snowy Taiga\",\n    \"biome.minecraft.snowy_taiga_hills\": \"Snowy Taiga Hills\",\n    \"biome.minecraft.taiga_hills\": \"Taiga Hills\",\n    \"biome.minecraft.taiga\": \"Taiga\",\n    \"biome.minecraft.the_void\": \"The Void\",\n    \"biome.minecraft.warm_ocean\": \"Warm Ocean\",\n    \"generator.minecraft.surface\": \"Surface\",\n    \"generator.minecraft.caves\": \"Caves\",\n    \"generator.minecraft.floating_islands\": \"Floating Islands\",\n    \"realms.missing.module.error.text\": \"Realms could not be opened right now, please try again later\",\n    \"realms.missing.snapshot.error.text\": \"Realms is currently not supported in snapshots\"\n  },\n  \"1.12\": {\n    \"potion.potency.3\": \"enchantment.level.4\",\n    \"potion.potency.4\": \"enchantment.level.5\",\n    \"potion.potency.5\": \"enchantment.level.6\",\n    \"gui.recipebook.moreRecipes\": \"Right Click for more\",\n    \"gui.recipebook.toggleRecipes.all\": \"Showing all\",\n    \"gui.recipebook.toggleRecipes.craftable\": \"Showing craftable\",\n    \"multiplayer.status.and_more\": \"... and %s more ...\",\n    \"multiplayer.status.cancelled\": \"Cancelled\",\n    \"multiplayer.status.cannot_connect\": \"Can't connect to server\",\n    \"multiplayer.status.cannot_resolve\": \"Can't resolve hostname\",\n    \"multiplayer.status.client_out_of_date\": \"Client out of date!\",\n    \"multiplayer.status.no_connection\": \"(no connection)\",\n    \"multiplayer.status.old\": \"Old\",\n    \"multiplayer.status.pinging\": \"Pinging...\",\n    \"multiplayer.status.server_out_of_date\": \"Server out of date!\",\n    \"multiplayer.status.unknown\": \"???\",\n    \"multiplayer.status.unrequested\": \"Received unrequested status\",\n    \"multiplayer.disconnect.authservers_down\": \"Authentication servers are down. Please try again later, sorry!\",\n    \"multiplayer.disconnect.banned\": \"You are banned from this server.\",\n    \"multiplayer.disconnect.duplicate_login\": \"You logged in from another location\",\n    \"multiplayer.disconnect.flying\": \"Flying is not enabled on this server\",\n    \"multiplayer.disconnect.generic\": \"Disconnected\",\n    \"multiplayer.disconnect.idling\": \"You have been idle for too long!\",\n    \"multiplayer.disconnect.illegal_characters\": \"Illegal characters in chat\",\n    \"multiplayer.disconnect.invalid_entity_attacked\": \"Attempting to attack an invalid entity\",\n    \"multiplayer.disconnect.invalid_player_movement\": \"Invalid move player packet received\",\n    \"multiplayer.disconnect.invalid_vehicle_movement\": \"Invalid move vehicle packet received\",\n    \"multiplayer.disconnect.ip_banned\": \"You have been IP banned.\",\n    \"multiplayer.disconnect.kicked\": \"Kicked by an operator.\",\n    \"multiplayer.disconnect.outdated_client\": \"Outdated client! Please use %s\",\n    \"multiplayer.disconnect.outdated_server\": \"Outdated server! I'm still on %s\",\n    \"multiplayer.disconnect.server_shutdown\": \"Server closed\",\n    \"multiplayer.disconnect.slow_login\": \"Took too long to log in\",\n    \"multiplayer.disconnect.unverified_username\": \"Failed to verify username!\",\n    \"chat.type.text.narrate\": \"%s says %s\",\n    \"chat.type.advancement.task\": \"%s has made the advancement %s\",\n    \"chat.type.advancement.challenge\": \"%s has completed the challenge %s\",\n    \"chat.type.advancement.goal\": \"%s has reached the goal %s\",\n    \"options.clouds.fancy\": \"Fancy\",\n    \"options.clouds.fast\": \"Fast\",\n    \"options.narrator\": \"Narrator\",\n    \"options.narrator.off\": \"Off\",\n    \"options.narrator.all\": \"Narrates all\",\n    \"options.narrator.chat\": \"Narrates chat\",\n    \"options.narrator.system\": \"Narrates system\",\n    \"options.narrator.notavailable\": \"Not available\",\n    \"narrator.toast.disabled\": \"Narrator Disabled\",\n    \"narrator.toast.enabled\": \"Narrator Enabled\",\n    \"key.mouse.left\": \"Left Click\",\n    \"key.mouse.middle\": \"Middle Click\",\n    \"key.mouse.right\": \"Right Click\",\n    \"key.saveToolbarActivator\": \"Save Toolbar Activator\",\n    \"key.loadToolbarActivator\": \"Load Toolbar Activator\",\n    \"key.advancements\": \"Advancements\",\n    \"key.categories.creative\": \"Creative Mode\",\n    \"tile.glazedTerracottaWhite.name\": \"White Glazed Terracotta\",\n    \"tile.glazedTerracottaOrange.name\": \"Orange Glazed Terracotta\",\n    \"tile.glazedTerracottaMagenta.name\": \"Magenta Glazed Terracotta\",\n    \"tile.glazedTerracottaLightBlue.name\": \"Light Blue Glazed Terracotta\",\n    \"tile.glazedTerracottaYellow.name\": \"Yellow Glazed Terracotta\",\n    \"tile.glazedTerracottaLime.name\": \"Lime Glazed Terracotta\",\n    \"tile.glazedTerracottaPink.name\": \"Pink Glazed Terracotta\",\n    \"tile.glazedTerracottaGray.name\": \"Gray Glazed Terracotta\",\n    \"tile.glazedTerracottaSilver.name\": \"Light Gray Glazed Terracotta\",\n    \"tile.glazedTerracottaCyan.name\": \"Cyan Glazed Terracotta\",\n    \"tile.glazedTerracottaPurple.name\": \"Purple Glazed Terracotta\",\n    \"tile.glazedTerracottaBlue.name\": \"Blue Glazed Terracotta\",\n    \"tile.glazedTerracottaBrown.name\": \"Brown Glazed Terracotta\",\n    \"tile.glazedTerracottaGreen.name\": \"Green Glazed Terracotta\",\n    \"tile.glazedTerracottaRed.name\": \"Red Glazed Terracotta\",\n    \"tile.glazedTerracottaBlack.name\": \"Black Glazed Terracotta\",\n    \"tile.concrete.black.name\": \"Black Concrete\",\n    \"tile.concrete.red.name\": \"Red Concrete\",\n    \"tile.concrete.green.name\": \"Green Concrete\",\n    \"tile.concrete.brown.name\": \"Brown Concrete\",\n    \"tile.concrete.blue.name\": \"Blue Concrete\",\n    \"tile.concrete.purple.name\": \"Purple Concrete\",\n    \"tile.concrete.cyan.name\": \"Cyan Concrete\",\n    \"tile.concrete.silver.name\": \"Light Gray Concrete\",\n    \"tile.concrete.gray.name\": \"Gray Concrete\",\n    \"tile.concrete.pink.name\": \"Pink Concrete\",\n    \"tile.concrete.lime.name\": \"Lime Concrete\",\n    \"tile.concrete.yellow.name\": \"Yellow Concrete\",\n    \"tile.concrete.lightBlue.name\": \"Light Blue Concrete\",\n    \"tile.concrete.magenta.name\": \"Magenta Concrete\",\n    \"tile.concrete.orange.name\": \"Orange Concrete\",\n    \"tile.concrete.white.name\": \"White Concrete\",\n    \"tile.concretePowder.black.name\": \"Black Concrete Powder\",\n    \"tile.concretePowder.red.name\": \"Red Concrete Powder\",\n    \"tile.concretePowder.green.name\": \"Green Concrete Powder\",\n    \"tile.concretePowder.brown.name\": \"Brown Concrete Powder\",\n    \"tile.concretePowder.blue.name\": \"Blue Concrete Powder\",\n    \"tile.concretePowder.purple.name\": \"Purple Concrete Powder\",\n    \"tile.concretePowder.cyan.name\": \"Cyan Concrete Powder\",\n    \"tile.concretePowder.silver.name\": \"Light Gray Concrete Powder\",\n    \"tile.concretePowder.gray.name\": \"Gray Concrete Powder\",\n    \"tile.concretePowder.pink.name\": \"Pink Concrete Powder\",\n    \"tile.concretePowder.lime.name\": \"Lime Concrete Powder\",\n    \"tile.concretePowder.yellow.name\": \"Yellow Concrete Powder\",\n    \"tile.concretePowder.lightBlue.name\": \"Light Blue Concrete Powder\",\n    \"tile.concretePowder.magenta.name\": \"Magenta Concrete Powder\",\n    \"tile.concretePowder.orange.name\": \"Orange Concrete Powder\",\n    \"tile.concretePowder.white.name\": \"White Concrete Powder\",\n    \"item.bed.black.name\": \"Black Bed\",\n    \"item.bed.red.name\": \"Red Bed\",\n    \"item.bed.green.name\": \"Green Bed\",\n    \"item.bed.brown.name\": \"Brown Bed\",\n    \"item.bed.blue.name\": \"Blue Bed\",\n    \"item.bed.purple.name\": \"Purple Bed\",\n    \"item.bed.cyan.name\": \"Cyan Bed\",\n    \"item.bed.silver.name\": \"Light Gray Bed\",\n    \"item.bed.gray.name\": \"Gray Bed\",\n    \"item.bed.pink.name\": \"Pink Bed\",\n    \"item.bed.lime.name\": \"Lime Bed\",\n    \"item.bed.yellow.name\": \"Yellow Bed\",\n    \"item.bed.lightBlue.name\": \"Light Blue Bed\",\n    \"item.bed.magenta.name\": \"Magenta Bed\",\n    \"item.bed.orange.name\": \"Orange Bed\",\n    \"item.bed.white.name\": \"White Bed\",\n    \"item.knowledgeBook.name\": \"Knowledge Book\",\n    \"container.enchant.level.requirement\": \"Level requirement: %s\",\n    \"filled_map.unknown\": \"Unknown Map\",\n    \"filled_map.level\": \"(Level %s/%s)\",\n    \"filled_map.scale\": \"Scaling at 1:%s\",\n    \"entity.Parrot.name\": \"Parrot\",\n    \"entity.IllusionIllager.name\": \"Illusioner\",\n    \"gui.advancements\": \"Advancements\",\n    \"advancements.empty\": \"There doesn't seem to be anything here...\",\n    \"advancements.toast.task\": \"Advancement Made!\",\n    \"advancements.toast.challenge\": \"Challenge Complete!\",\n    \"advancements.toast.goal\": \"Goal Reached!\",\n    \"recipe.toast.title\": \"New Recipes Unlocked!\",\n    \"recipe.toast.description\": \"Check your recipe book\",\n    \"commands.advancement.usage\": \"/advancement <grant|revoke|test> <player>\",\n    \"commands.advancement.advancementNotFound\": \"No advancement was found by the name '%1$s'\",\n    \"commands.advancement.criterionNotFound\": \"The advancement '%1$s' does not contain the criterion '%2$s'\",\n    \"commands.reload.usage\": \"/reload\",\n    \"commands.reload.success\": \"Successfully reloaded loot tables, advancements and functions\",\n    \"commands.function.usage\": \"/function <name> [if <selector>|unless <selector>]\",\n    \"commands.function.unknown\": \"Unknown function '%s'\",\n    \"commands.function.success\": \"Executed %2$s command(s) from function '%1$s'\",\n    \"commands.function.skipped\": \"Skipped execution of function '%1$s'\",\n    \"commands.advancement.grant.usage\": \"/advancement grant <player> <only|until|from|through|everything>\",\n    \"commands.advancement.grant.only.usage\": \"/advancement grant <player> only <advancement> [criterion]\",\n    \"commands.advancement.grant.only.failed\": \"Couldn't grant the advancement '%1$s' to %2$s because they already have it\",\n    \"commands.advancement.grant.only.success\": \"Granted the entire advancement '%1$s' to %2$s\",\n    \"commands.advancement.grant.criterion.failed\": \"Couldn't grant the criterion '%3$s' of advancement '%1$s' to %2$s because they already have it\",\n    \"commands.advancement.grant.criterion.success\": \"Granted the criterion '%3$s' of advancement '%1$s' to %2$s\",\n    \"commands.advancement.grant.until.usage\": \"/advancement grant <player> until <advancement>\",\n    \"commands.advancement.grant.until.failed\": \"Couldn't grant the advancement '%1$s' or its ancestors to %2$s because they already have them all\",\n    \"commands.advancement.grant.until.success\": \"Granted '%1$s' and all ancestors (%3$s total granted) to %2$s\",\n    \"commands.advancement.grant.from.usage\": \"/advancement grant <player> from <advancement>\",\n    \"commands.advancement.grant.from.failed\": \"Couldn't grant the advancement '%1$s' or its descendants to %2$s because they already have them all\",\n    \"commands.advancement.grant.from.success\": \"Granted '%1$s' and all descendants (%3$s total granted) to %2$s\",\n    \"commands.advancement.grant.through.usage\": \"/advancement grant <player> through <advancement>\",\n    \"commands.advancement.grant.through.failed\": \"Couldn't grant the advancement '%1$s', its ancestors or its descendants to %2$s because they already have them all\",\n    \"commands.advancement.grant.through.success\": \"Granted '%1$s', all ancestors and all descendants (%3$s total granted) to %2$s\",\n    \"commands.advancement.grant.everything.usage\": \"/advancement grant <player> everything\",\n    \"commands.advancement.grant.everything.failed\": \"Couldn't grant any advancements to %1$s because they already have them all\",\n    \"commands.advancement.grant.everything.success\": \"Granted every advancement (%2$s total granted) to %1$s\",\n    \"commands.advancement.revoke.usage\": \"/advancement revoke <player> <only|until|from|through|everything>\",\n    \"commands.advancement.revoke.only.usage\": \"/advancement revoke <player> only <advancement> [criterion]\",\n    \"commands.advancement.revoke.only.failed\": \"Couldn't revoke the advancement '%1$s' from %2$s because they haven't started it\",\n    \"commands.advancement.revoke.only.success\": \"Revoked the entire advancement '%1$s' from %2$s\",\n    \"commands.advancement.revoke.criterion.failed\": \"Couldn't revoke the criterion '%3$s' of advancement '%1$s' from %2$s because they haven't started it\",\n    \"commands.advancement.revoke.criterion.success\": \"Revoked the criterion '%3$s' of advancement '%1$s' from %2$s\",\n    \"commands.advancement.revoke.until.usage\": \"/advancement revoke <player> until <advancement>\",\n    \"commands.advancement.revoke.until.failed\": \"Couldn't revoke the advancement '%1$s' or its ancestors from %2$s because they haven't started any\",\n    \"commands.advancement.revoke.until.success\": \"Revoked '%1$s' and all ancestors (%3$s total revoked) from %2$s\",\n    \"commands.advancement.revoke.from.usage\": \"/advancement revoke <player> from <advancement>\",\n    \"commands.advancement.revoke.from.failed\": \"Couldn't revoke the advancement '%1$s' or its descendants from %2$s because they haven't started any\",\n    \"commands.advancement.revoke.from.success\": \"Revoked '%1$s' and all descendants (%3$s total revoked) from %2$s\",\n    \"commands.advancement.revoke.through.usage\": \"/advancement revoke <player> through <advancement>\",\n    \"commands.advancement.revoke.through.failed\": \"Couldn't revoke the advancement '%1$s', its ancestors or its descendants from %2$s because they haven't started any\",\n    \"commands.advancement.revoke.through.success\": \"Revoked '%1$s', all ancestors and all descendants (%3$s total revoked) from %2$s\",\n    \"commands.advancement.revoke.everything.usage\": \"/advancement revoke <player> everything\",\n    \"commands.advancement.revoke.everything.failed\": \"Couldn't revoke any advancements to %1$s because they haven't started any\",\n    \"commands.advancement.revoke.everything.success\": \"Revoked every advancement (%2$s total revoked) from %1$s\",\n    \"commands.advancement.test.usage\": \"/advancement test <player> <advancement> [criterion]\",\n    \"commands.advancement.test.criterion.success\": \"Player %1$s has completed criterion '%3$s' of advancement '%2$s'\",\n    \"commands.advancement.test.criterion.notDone\": \"Player %1$s has not completed criterion '%3$s' of advancement '%2$s'\",\n    \"commands.advancement.test.advancement.success\": \"Player %1$s has completed advancement '%2$s'\",\n    \"commands.advancement.test.advancement.notDone\": \"Player %1$s has not completed advancement '%2$s'\",\n    \"commands.recipe.usage\": \"/recipe <give|take> [player] <name|*>\",\n    \"commands.recipe.alreadyHave\": \"Player %s already has a recipe for %s\",\n    \"commands.recipe.dontHave\": \"Player %s doesn't have the recipe for %s\",\n    \"commands.recipe.give.success.all\": \"Successfully given all recipes to %s\",\n    \"commands.recipe.give.success.one\": \"Successfully given %s the recipe for %s\",\n    \"commands.recipe.take.success.all\": \"Successfully taken all recipes from %s\",\n    \"commands.recipe.take.success.one\": \"Successfully removed the recipe for %s from %s\",\n    \"commands.recipe.unknownrecipe\": \"%s is an unknown recipe\",\n    \"commands.recipe.unsupported\": \"%s is an unsupported recipe\",\n    \"itemGroup.hotbar\": \"Saved Toolbars\",\n    \"inventory.hotbarSaved\": \"Item toolbar saved (restore with %1$s+%2$s)\",\n    \"inventory.hotbarInfo\": \"Save toolbar with %1$s+%2$s\",\n    \"advMode.self\": \"Use \\\"@s\\\" to target the executing entity\",\n    \"subtitles.entity.parrot.ambient\": \"Parrot talks\",\n    \"subtitles.entity.parrot.death\": \"Parrot dies\",\n    \"subtitles.entity.parrot.eats\": \"Parrot eats\",\n    \"subtitles.entity.parrot.hurts\": \"Parrot hurts\",\n    \"subtitles.entity.parrot.imitate.blaze\": \"Parrot breathes\",\n    \"subtitles.entity.parrot.imitate.cave_spider\": \"Parrot hisses\",\n    \"subtitles.entity.parrot.imitate.creeper\": \"Parrot hisses\",\n    \"subtitles.entity.parrot.imitate.elder_guardian\": \"Parrot flaps\",\n    \"subtitles.entity.parrot.imitate.enderdragon\": \"Parrot roars\",\n    \"subtitles.entity.parrot.imitate.enderman\": \"Parrot vwoops\",\n    \"subtitles.entity.parrot.imitate.endermite\": \"Parrot scuttles\",\n    \"subtitles.entity.parrot.imitate.evocation_illager\": \"Parrot murmurs\",\n    \"subtitles.entity.parrot.imitate.ghast\": \"Parrot cries\",\n    \"subtitles.entity.parrot.imitate.husk\": \"Parrot groans\",\n    \"subtitles.entity.parrot.imitate.illusion_illager\": \"Parrot murmurs\",\n    \"subtitles.entity.parrot.imitate.magmacube\": \"Parrot squishes\",\n    \"subtitles.entity.parrot.imitate.polar_bear\": \"Parrot groans\",\n    \"subtitles.entity.parrot.imitate.shulker\": \"Parrot lurks\",\n    \"subtitles.entity.parrot.imitate.silverfish\": \"Parrot hisses\",\n    \"subtitles.entity.parrot.imitate.skeleton\": \"Parrot rattles\",\n    \"subtitles.entity.parrot.imitate.slime\": \"Parrot squishes\",\n    \"subtitles.entity.parrot.imitate.spider\": \"Parrot hisses\",\n    \"subtitles.entity.parrot.imitate.stray\": \"Parrot rattles\",\n    \"subtitles.entity.parrot.imitate.vex\": \"Parrot vexes\",\n    \"subtitles.entity.parrot.imitate.vindication_illager\": \"Parrot mutters\",\n    \"subtitles.entity.parrot.imitate.witch\": \"Parrot giggles\",\n    \"subtitles.entity.parrot.imitate.wither\": \"Parrot angers\",\n    \"subtitles.entity.parrot.imitate.wither_skeleton\": \"Parrot rattles\",\n    \"subtitles.entity.parrot.imitate.wolf\": \"Parrot pants\",\n    \"subtitles.entity.parrot.imitate.zombie\": \"Parrot groans\",\n    \"subtitles.entity.parrot.imitate.zombie_pigman\": \"Parrot grunts\",\n    \"subtitles.entity.parrot.imitate.zombie_villager\": \"Parrot groans\",\n    \"subtitles.entity.illusion_illager.ambient\": \"Illusioner murmurs\",\n    \"subtitles.entity.illusion_illager.cast_spell\": \"Illusioner casts spell\",\n    \"subtitles.entity.illusion_illager.death\": \"Illusioner dies\",\n    \"subtitles.entity.illusion_illager.hurt\": \"Illusioner hurts\",\n    \"subtitles.entity.illusion_illager.mirror_move\": \"Illusioner displaces\",\n    \"subtitles.entity.illusion_illager.prepare_blindness\": \"Illusioner prepares blindness\",\n    \"subtitles.entity.illusion_illager.prepare_mirror\": \"Illusioner prepares mirror image\",\n    \"tutorial.move.title\": \"Move with %s, %s, %s and %s\",\n    \"tutorial.move.description\": \"Jump with %s\",\n    \"tutorial.look.title\": \"Look around\",\n    \"tutorial.look.description\": \"Use your mouse to turn\",\n    \"tutorial.find_tree.title\": \"Find a tree\",\n    \"tutorial.find_tree.description\": \"Punch it to collect wood\",\n    \"tutorial.punch_tree.title\": \"Destroy the tree\",\n    \"tutorial.punch_tree.description\": \"Hold down %s\",\n    \"tutorial.open_inventory.title\": \"Open your inventory\",\n    \"tutorial.open_inventory.description\": \"Press %s\",\n    \"tutorial.craft_planks.title\": \"Craft wooden planks\",\n    \"tutorial.craft_planks.description\": \"The recipe book can help\",\n    \"advancements.adventure.adventuring_time.title\": \"Adventuring Time\",\n    \"advancements.adventure.adventuring_time.description\": \"Discover every biome\",\n    \"advancements.adventure.kill_all_mobs.title\": \"Monsters Hunted\",\n    \"advancements.adventure.kill_all_mobs.description\": \"Kill one of every hostile monster\",\n    \"advancements.adventure.kill_a_mob.title\": \"Monster Hunter\",\n    \"advancements.adventure.kill_a_mob.description\": \"Kill any hostile monster\",\n    \"advancements.adventure.root.title\": \"Adventure\",\n    \"advancements.adventure.root.description\": \"Adventure, exploration and combat\",\n    \"advancements.adventure.shoot_arrow.title\": \"Take Aim\",\n    \"advancements.adventure.shoot_arrow.description\": \"Shoot something with a bow and arrow\",\n    \"advancements.adventure.sleep_in_bed.title\": \"Sweet dreams\",\n    \"advancements.adventure.sleep_in_bed.description\": \"Change your respawn point\",\n    \"advancements.adventure.sniper_duel.title\": \"Sniper duel\",\n    \"advancements.adventure.sniper_duel.description\": \"Kill a skeleton with an arrow from more than 50 meters\",\n    \"advancements.adventure.trade.title\": \"What a Deal!\",\n    \"advancements.adventure.trade.description\": \"Successfully trade with a Villager\",\n    \"advancements.adventure.summon_iron_golem.title\": \"Hired Help\",\n    \"advancements.adventure.summon_iron_golem.description\": \"Summon an Iron Golem to help defend a village\",\n    \"advancements.adventure.totem_of_undying.title\": \"Postmortal\",\n    \"advancements.adventure.totem_of_undying.description\": \"Use a Totem of Undying to cheat death\",\n    \"advancements.husbandry.root.title\": \"Husbandry\",\n    \"advancements.husbandry.root.description\": \"The world is full of friends and food\",\n    \"advancements.husbandry.breed_an_animal.title\": \"The Parrots and the Bats\",\n    \"advancements.husbandry.breed_an_animal.description\": \"Breed two animals together\",\n    \"advancements.husbandry.breed_all_animals.title\": \"Two by Two\",\n    \"advancements.husbandry.breed_all_animals.description\": \"Breed all the animals!\",\n    \"advancements.husbandry.tame_an_animal.title\": \"Best Friends Forever\",\n    \"advancements.husbandry.tame_an_animal.description\": \"Tame an animal\",\n    \"advancements.husbandry.plant_seed.title\": \"A Seedy Place\",\n    \"advancements.husbandry.plant_seed.description\": \"Plant a seed and watch it grow\",\n    \"advancements.husbandry.break_diamond_hoe.title\": \"Serious Dedication\",\n    \"advancements.husbandry.break_diamond_hoe.description\": \"Completely use up a diamond hoe, and then reevaluate your life choices\",\n    \"advancements.husbandry.balanced_diet.title\": \"A Balanced Diet\",\n    \"advancements.husbandry.balanced_diet.description\": \"Eat everything that is edible, even if it's not good for you\",\n    \"advancements.end.dragon_breath.title\": \"You Need a Mint\",\n    \"advancements.end.dragon_breath.description\": \"Collect dragon's breath in a glass bottle\",\n    \"advancements.end.dragon_egg.title\": \"The Next Generation\",\n    \"advancements.end.dragon_egg.description\": \"Hold the Dragon Egg\",\n    \"advancements.end.elytra.title\": \"Sky's the Limit\",\n    \"advancements.end.elytra.description\": \"Find an Elytra\",\n    \"advancements.end.enter_end_gateway.title\": \"Remote Getaway\",\n    \"advancements.end.enter_end_gateway.description\": \"Escape the island\",\n    \"advancements.end.find_end_city.title\": \"The City at the End of the Game\",\n    \"advancements.end.find_end_city.description\": \"Go on in, what could happen?\",\n    \"advancements.end.kill_dragon.title\": \"Free the End\",\n    \"advancements.end.kill_dragon.description\": \"Good luck\",\n    \"advancements.end.levitate.title\": \"Great View From Up Here\",\n    \"advancements.end.levitate.description\": \"Levitate up 50 blocks from the attacks of a Shulker\",\n    \"advancements.end.respawn_dragon.title\": \"The End... Again...\",\n    \"advancements.end.respawn_dragon.description\": \"Respawn the ender dragon\",\n    \"advancements.end.root.title\": \"The End\",\n    \"advancements.end.root.description\": \"Or the beginning?\",\n    \"advancements.nether.brew_potion.title\": \"Local Brewery\",\n    \"advancements.nether.brew_potion.description\": \"Brew a potion\",\n    \"advancements.nether.all_potions.title\": \"A Furious Cocktail\",\n    \"advancements.nether.all_potions.description\": \"Have every potion effect applied at the same time\",\n    \"advancements.nether.all_effects.title\": \"How Did We Get Here?\",\n    \"advancements.nether.all_effects.description\": \"Have every effect applied at the same time\",\n    \"advancements.nether.create_beacon.title\": \"Bring Home the Beacon\",\n    \"advancements.nether.create_beacon.description\": \"Construct and place a Beacon\",\n    \"advancements.nether.create_full_beacon.title\": \"Beaconator\",\n    \"advancements.nether.create_full_beacon.description\": \"Bring a beacon to full power\",\n    \"advancements.nether.find_fortress.title\": \"A Terrible Fortress\",\n    \"advancements.nether.find_fortress.description\": \"Break your way into a Nether Fortress\",\n    \"advancements.nether.get_wither_skull.title\": \"Spooky Scary Skeleton\",\n    \"advancements.nether.get_wither_skull.description\": \"Obtain a wither skeleton's skull\",\n    \"advancements.nether.obtain_blaze_rod.title\": \"Into Fire\",\n    \"advancements.nether.obtain_blaze_rod.description\": \"Relieve a Blaze of its rod\",\n    \"advancements.nether.return_to_sender.title\": \"Return to Sender\",\n    \"advancements.nether.return_to_sender.description\": \"Destroy a Ghast with a fireball\",\n    \"advancements.nether.root.title\": \"Nether\",\n    \"advancements.nether.root.description\": \"Bring summer clothes\",\n    \"advancements.nether.summon_wither.title\": \"Withering Heights\",\n    \"advancements.nether.summon_wither.description\": \"Summon the Wither\",\n    \"advancements.nether.fast_travel.title\": \"Subspace Bubble\",\n    \"advancements.nether.fast_travel.description\": \"Use the Nether to travel 7km in the Overworld\",\n    \"advancements.nether.uneasy_alliance.title\": \"Uneasy Alliance\",\n    \"advancements.nether.uneasy_alliance.description\": \"Rescue a Ghast from the Nether, bring it safely home to the Overworld... and then kill it.\",\n    \"advancements.story.cure_zombie_villager.title\": \"Zombie Doctor\",\n    \"advancements.story.cure_zombie_villager.description\": \"Weaken and then cure a zombie villager\",\n    \"advancements.story.deflect_arrow.title\": \"Not Today, Thank You\",\n    \"advancements.story.deflect_arrow.description\": \"Deflect an arrow with a shield\",\n    \"advancements.story.enchant_item.title\": \"Enchanter\",\n    \"advancements.story.enchant_item.description\": \"Enchant an item at an Enchanting Table\",\n    \"advancements.story.enter_the_end.title\": \"The End?\",\n    \"advancements.story.enter_the_end.description\": \"Enter the End Portal\",\n    \"advancements.story.enter_the_nether.title\": \"We Need to Go Deeper\",\n    \"advancements.story.enter_the_nether.description\": \"Build, light and enter a Nether Portal\",\n    \"advancements.story.follow_ender_eye.title\": \"Eye Spy\",\n    \"advancements.story.follow_ender_eye.description\": \"Follow an Ender Eye\",\n    \"advancements.story.form_obsidian.title\": \"Ice Bucket Challenge\",\n    \"advancements.story.form_obsidian.description\": \"Form and mine a block of Obsidian\",\n    \"advancements.story.iron_tools.title\": \"Isn't It Iron Pick\",\n    \"advancements.story.iron_tools.description\": \"Upgrade your pickaxe\",\n    \"advancements.story.lava_bucket.title\": \"Hot Stuff\",\n    \"advancements.story.lava_bucket.description\": \"Fill a bucket with lava\",\n    \"advancements.story.mine_diamond.title\": \"Diamonds!\",\n    \"advancements.story.mine_diamond.description\": \"Acquire diamonds\",\n    \"advancements.story.mine_stone.title\": \"Stone Age\",\n    \"advancements.story.mine_stone.description\": \"Mine stone with your new pickaxe\",\n    \"advancements.story.obtain_armor.title\": \"Suit Up\",\n    \"advancements.story.obtain_armor.description\": \"Protect yourself with a piece of iron armor\",\n    \"advancements.story.root.title\": \"Minecraft\",\n    \"advancements.story.root.description\": \"The heart and story of the game\",\n    \"advancements.story.shiny_gear.title\": \"Cover Me With Diamonds\",\n    \"advancements.story.shiny_gear.description\": \"Diamond armor saves lives\",\n    \"advancements.story.smelt_iron.title\": \"Acquire Hardware\",\n    \"advancements.story.smelt_iron.description\": \"Smelt an iron ingot\",\n    \"advancements.story.upgrade_tools.title\": \"Getting an Upgrade\",\n    \"advancements.story.upgrade_tools.description\": \"Construct a better pickaxe\"\n  },\n  \"1.11\": {\n    \"selectWorld.unable_to_load\": \"Unable to load worlds\",\n    \"selectWorld.load_folder_access\": \"Unable to read or access folder where game worlds are saved!\",\n    \"createWorld.customize.preset.classic_flat\": \"Classic Flat\",\n    \"createWorld.customize.preset.tunnelers_dream\": \"Tunnelers' Dream\",\n    \"createWorld.customize.preset.water_world\": \"Water World\",\n    \"createWorld.customize.preset.overworld\": \"Overworld\",\n    \"createWorld.customize.preset.snowy_kingdom\": \"Snowy Kingdom\",\n    \"createWorld.customize.preset.bottomless_pit\": \"Bottomless Pit\",\n    \"createWorld.customize.preset.desert\": \"Desert\",\n    \"createWorld.customize.preset.redstone_ready\": \"Redstone Ready\",\n    \"createWorld.customize.preset.the_void\": \"The Void\",\n    \"createWorld.customize.custom.useMansions\": \"Woodland Mansions\",\n    \"spectatorMenu.previous_page\": \"Previous Page\",\n    \"spectatorMenu.next_page\": \"Next Page\",\n    \"spectatorMenu.close\": \"Close Menu\",\n    \"spectatorMenu.teleport\": \"Teleport to Player\",\n    \"spectatorMenu.teleport.prompt\": \"Select a player to teleport to\",\n    \"spectatorMenu.team_teleport\": \"Teleport to Team Member\",\n    \"spectatorMenu.team_teleport.prompt\": \"Select a team to teleport to\",\n    \"spectatorMenu.root.prompt\": \"Press a key to select a command, and again to use it.\",\n    \"options.chunks\": \"%s chunks\",\n    \"options.framerate\": \"%s fps\",\n    \"title.oldjava1\": \"Old java detected; this will prevent you from playing\",\n    \"title.oldjava2\": \"in the future as Java 8 will be required.\",\n    \"tile.air.name\": \"Air\",\n    \"tile.bed.tooFarAway\": \"You may not rest now, the bed is too far away\",\n    \"tile.observer.name\": \"Observer\",\n    \"tile.shulkerBoxWhite.name\": \"White Shulker Box\",\n    \"tile.shulkerBoxOrange.name\": \"Orange Shulker Box\",\n    \"tile.shulkerBoxMagenta.name\": \"Magenta Shulker Box\",\n    \"tile.shulkerBoxLightBlue.name\": \"Light Blue Shulker Box\",\n    \"tile.shulkerBoxYellow.name\": \"Yellow Shulker Box\",\n    \"tile.shulkerBoxLime.name\": \"Lime Shulker Box\",\n    \"tile.shulkerBoxPink.name\": \"Pink Shulker Box\",\n    \"tile.shulkerBoxGray.name\": \"Gray Shulker Box\",\n    \"tile.shulkerBoxSilver.name\": \"Light Gray Shulker Box\",\n    \"tile.shulkerBoxCyan.name\": \"Cyan Shulker Box\",\n    \"tile.shulkerBoxPurple.name\": \"Purple Shulker Box\",\n    \"tile.shulkerBoxBlue.name\": \"Blue Shulker Box\",\n    \"tile.shulkerBoxBrown.name\": \"Brown Shulker Box\",\n    \"tile.shulkerBoxGreen.name\": \"Green Shulker Box\",\n    \"tile.shulkerBoxRed.name\": \"Red Shulker Box\",\n    \"tile.shulkerBoxBlack.name\": \"Black Shulker Box\",\n    \"item.splash_potion.name\": \"Splash Potion\",\n    \"item.lingering_potion.name\": \"Lingering Potion\",\n    \"container.shulkerBox\": \"Shulker Box\",\n    \"container.shulkerBox.more\": \"and %s more...\",\n    \"item.color\": \"Color: %s\",\n    \"item.nbt_tags\": \"NBT: %s tag(s)\",\n    \"item.durability\": \"Durability: %s / %s\",\n    \"filled_map.mansion\": \"Woodland Explorer Map\",\n    \"filled_map.monument\": \"Ocean Explorer Map\",\n    \"entity.ZombieVillager.name\": \"Zombie Villager\",\n    \"entity.ElderGuardian.name\": \"Elder Guardian\",\n    \"entity.EvocationIllager.name\": \"Evoker\",\n    \"entity.Vex.name\": \"Vex\",\n    \"entity.VindicationIllager.name\": \"Vindicator\",\n    \"entity.Villager.nitwit\": \"Nitwit\",\n    \"entity.Villager.cartographer\": \"Cartographer\",\n    \"entity.Horse.name\": \"Horse\",\n    \"entity.Llama.name\": \"Llama\",\n    \"death.attack.cramming\": \"%1$s was squished too much\",\n    \"death.attack.fireworks\": \"%1$s went off with a bang\",\n    \"enchantment.sweeping\": \"Sweeping Edge\",\n    \"enchantment.binding_curse\": \"Curse of Binding\",\n    \"enchantment.vanishing_curse\": \"Curse of Vanishing\",\n    \"stat.shulkerBoxOpened\": \"Shulker Boxes Opened\",\n    \"commands.generic.blockstate.invalid\": \"'%s' is not a state for block %s\",\n    \"commands.generic.selector_argument\": \"Invalid selector argument: '%s'\",\n    \"commands.generic.player.unspecified\": \"You must specify which player you wish to perform this action on.\",\n    \"commands.save.flushStart\": \"Flushing all saves...\",\n    \"commands.save.flushEnd\": \"Flushing completed\",\n    \"commands.scoreboard.players.tag.tagError\": \"Players tag command failed, reason: %s\",\n    \"commands.spreadplayers.noop\": \"No players found to spread\",\n    \"commands.locate.usage\": \"/locate <feature>\",\n    \"commands.locate.success\": \"Located %s at %s (y?) %s\",\n    \"commands.locate.failure\": \"Unable to locate any %s feature\",\n    \"subtitles.block.shulker_box.close\": \"Shulker closes\",\n    \"subtitles.block.shulker_box.open\": \"Shulker opens\",\n    \"subtitles.entity.elder_guardian.ambient.land\": \"Elder Guardian flaps\",\n    \"subtitles.entity.elder_guardian.ambient\": \"Elder Guardian moans\",\n    \"subtitles.entity.elder_guardian.attack\": \"Elder Guardian shoots\",\n    \"subtitles.entity.elder_guardian.curse\": \"Elder Guardian curses\",\n    \"subtitles.entity.elder_guardian.death\": \"Elder Guardian dies\",\n    \"subtitles.entity.elder_guardian.flop\": \"Elder Guardian flops\",\n    \"subtitles.entity.elder_guardian.hurt\": \"Elder Guardian hurts\",\n    \"subtitles.entity.evocation_fangs.attack\": \"Fangs snap\",\n    \"subtitles.entity.evocation_illager.ambient\": \"Evoker murmurs\",\n    \"subtitles.entity.evocation_illager.cast_spell\": \"Evoker casts spell\",\n    \"subtitles.entity.evocation_illager.death\": \"Evoker dies\",\n    \"subtitles.entity.evocation_illager.hurt\": \"Evoker hurts\",\n    \"subtitles.entity.evocation_illager.prepare_attack\": \"Evoker prepares attack\",\n    \"subtitles.entity.evocation_illager.prepare_summon\": \"Evoker prepares summoning\",\n    \"subtitles.entity.evocation_illager.prepare_wololo\": \"Evoker prepares charming\",\n    \"subtitles.entity.llama.ambient\": \"Llama bleats\",\n    \"subtitles.entity.llama.angry\": \"Llama bleats angry\",\n    \"subtitles.entity.llama.chest\": \"Llama Chest equips\",\n    \"subtitles.entity.llama.death\": \"Llama dies\",\n    \"subtitles.entity.llama.eat\": \"Llama eats\",\n    \"subtitles.entity.llama.hurt\": \"Llama hurts\",\n    \"subtitles.entity.llama.spit\": \"Llama spits\",\n    \"subtitles.entity.llama.step\": \"Llama steps\",\n    \"subtitles.entity.llama.swag\": \"Llama is decorated\",\n    \"subtitles.entity.mule.chest\": \"Mule Chest equips\",\n    \"subtitles.entity.vex.ambient\": \"Vex vexes\",\n    \"subtitles.entity.vex.charge\": \"Vex shrieks\",\n    \"subtitles.entity.vex.death\": \"Vex dies\",\n    \"subtitles.entity.vex.hurt\": \"Vex hurts\",\n    \"subtitles.entity.vindication_illager.ambient\": \"Vindicator mutters\",\n    \"subtitles.entity.vindication_illager.death\": \"Vindicator dies\",\n    \"subtitles.entity.vindication_illager.hurt\": \"Vindicator hurts\",\n    \"subtitles.entity.zombie_villager.ambient\": \"Zombie Villager groans\",\n    \"subtitles.entity.zombie_villager.death\": \"Zombie Villager dies\",\n    \"subtitles.entity.zombie_villager.hurt\": \"Zombie Villager hurts\",\n    \"subtitles.item.armor.equip_elytra\": \"Elytra rustles\",\n    \"subtitles.item.totem.use\": \"Totem activates\",\n    \"debug.reload_chunks.help\": \"F3 + A \",\n    \"debug.show_hitboxes.help\": \"F3 + B \",\n    \"debug.clear_chat.help\": \"F3 + D \",\n    \"debug.cycle_renderdistance.help\": \"F3 + F \",\n    \"debug.chunk_boundaries.help\": \"F3 + G \",\n    \"debug.advanced_tooltips.help\": \"F3 + H \",\n    \"debug.creative_spectator.help\": \"F3 + N \",\n    \"debug.pause_focus.help\": \"F3 + P \",\n    \"debug.help.help\": \"F3 + Q \",\n    \"debug.reload_resourcepacks.help\": \"F3 + T \",\n    \"debug.reload_chunks.message\": \"Reloading all chunks\",\n    \"debug.show_hitboxes.on\": \"Hitboxes: shown\",\n    \"debug.show_hitboxes.off\": \"Hitboxes: hidden\",\n    \"debug.cycle_renderdistance.message\": \"Render Distance: %s\",\n    \"debug.chunk_boundaries.on\": \"Chunk borders: shown\",\n    \"debug.chunk_boundaries.off\": \"Chunk borders: hidden\",\n    \"debug.advanced_tooltips.on\": \"Advanced tooltips: shown\",\n    \"debug.advanced_tooltips.off\": \"Advanced tooltips: hidden\",\n    \"debug.creative_spectator.error\": \"Unable to switch gamemode, no permission\",\n    \"debug.pause_focus.on\": \"Pause on lost focus: enabled\",\n    \"debug.pause_focus.off\": \"Pause on lost focus: disabled\",\n    \"debug.help.message\": \"Key bindings:\",\n    \"debug.reload_resourcepacks.message\": \"Reloaded resource packs\",\n    \"resourcepack.downloading\": \"Downloading Resource Pack\",\n    \"resourcepack.requesting\": \"Making Request...\",\n    \"resourcepack.progress\": \"Downloading file (%s MB)...\"\n  },\n  \"1.10\": {\n    \"options.autoJump\": \"Auto-jump\",\n    \"tile.magma.name\": \"Magma Block\",\n    \"tile.netherWartBlock.name\": \"Nether Wart Block\",\n    \"tile.redNetherBrick.name\": \"Red Nether Brick\",\n    \"tile.boneBlock.name\": \"Bone Block\",\n    \"tile.structureVoid.name\": \"Structure Void\",\n    \"structure_block.save_success\": \"Structure saved as '%s'\",\n    \"structure_block.save_failure\": \"Unable to save structure '%s'\",\n    \"structure_block.load_success\": \"Structure loaded from '%s'\",\n    \"structure_block.load_prepare\": \"Structure '%s' position prepared\",\n    \"structure_block.load_not_found\": \"Structure '%s' is not available\",\n    \"structure_block.size_success\": \"Size successfully detected for '%s'\",\n    \"structure_block.size_failure\": \"Unable to detect structure size, add corners with matching structure names\",\n    \"structure_block.mode.save\": \"[S]\",\n    \"structure_block.mode.load\": \"[L]\",\n    \"structure_block.mode.data\": \"[D]\",\n    \"structure_block.mode.corner\": \"[C]\",\n    \"structure_block.hover.save\": \"Save: %s\",\n    \"structure_block.hover.load\": \"Load: %s\",\n    \"structure_block.hover.data\": \"Data: %s\",\n    \"structure_block.hover.corner\": \"Corner: %s\",\n    \"structure_block.mode_info.save\": \"Save mode - write to file\",\n    \"structure_block.mode_info.load\": \"Load mode - load from file\",\n    \"structure_block.mode_info.data\": \"Data mode - game logic marker\",\n    \"structure_block.mode_info.corner\": \"Corner mode - placement and size marker\",\n    \"structure_block.structure_name\": \"Structure Name\",\n    \"structure_block.custom_data\": \"Custom Data Tag Name\",\n    \"structure_block.position\": \"Relative Position\",\n    \"structure_block.size\": \"Structure Size\",\n    \"structure_block.integrity\": \"Structure Integrity and Seed\",\n    \"structure_block.include_entities\": \"Include entities:\",\n    \"structure_block.detect_size\": \"Detect structure size and position:\",\n    \"structure_block.button.detect_size\": \"DETECT\",\n    \"structure_block.button.save\": \"SAVE\",\n    \"structure_block.button.load\": \"LOAD\",\n    \"structure_block.show_air\": \"Show invisible blocks:\",\n    \"structure_block.show_boundingbox\": \"Show bounding box:\",\n    \"entity.WitherSkeleton.name\": \"Wither Skeleton\",\n    \"entity.Stray.name\": \"Stray\",\n    \"entity.Husk.name\": \"Husk\",\n    \"entity.PolarBear.name\": \"Polar Bear\",\n    \"entity.Donkey.name\": \"Donkey\",\n    \"entity.Mule.name\": \"Mule\",\n    \"entity.SkeletonHorse.name\": \"Skeleton Horse\",\n    \"entity.ZombieHorse.name\": \"Zombie Horse\",\n    \"death.attack.hotFloor\": \"%1$s discovered floor was lava\",\n    \"death.attack.hotFloor.player\": \"%1$s walked into danger zone due to %2$s\",\n    \"commands.teleport.usage\": \"/teleport <entity> <x> <y> <z> [<y-rot> <x-rot>]\",\n    \"commands.teleport.success.coordinates\": \"Teleported %s to %s, %s, %s\",\n    \"subtitles.entity.husk.ambient\": \"Husk groans\",\n    \"subtitles.entity.husk.death\": \"Husk dies\",\n    \"subtitles.entity.husk.hurt\": \"Husk hurts\",\n    \"subtitles.entity.polar_bear.ambient\": \"Polar Bear groans\",\n    \"subtitles.entity.polar_bear.baby_ambient\": \"Polar Bear hums\",\n    \"subtitles.entity.polar_bear.death\": \"Polar Bear dies\",\n    \"subtitles.entity.polar_bear.hurt\": \"Polar Bear hurts\",\n    \"subtitles.entity.polar_bear.warning\": \"Polar Bear roars\",\n    \"subtitles.entity.stray.ambient\": \"Stray rattles\",\n    \"subtitles.entity.stray.death\": \"Stray dies\",\n    \"subtitles.entity.stray.hurt\": \"Stray hurts\",\n    \"subtitles.entity.wither_skeleton.ambient\": \"Wither Skeleton rattles\",\n    \"subtitles.entity.wither_skeleton.death\": \"Wither Skeleton dies\",\n    \"subtitles.entity.wither_skeleton.hurt\": \"Wither Skeleton hurts\"\n  },\n  \"1.9.3\": {\n    \"commands.stopsound.usage\": \"/stopsound <player> [source] [sound]\",\n    \"commands.stopsound.unknownSoundSource\": \"Source %s is unknown\",\n    \"commands.stopsound.success.individualSound\": \"Stopped sound '%s' with source '%s' for %s\",\n    \"commands.stopsound.success.soundSource\": \"Stopped source '%s' for %s\",\n    \"commands.stopsound.success.all\": \"Stopped all sounds for %s\"\n  },\n  \"1.9.1\": {\n    \"entity.MinecartHopper.name\": \"Minecart with Hopper\",\n    \"entity.MinecartChest.name\": \"Minecart with Chest\",\n    \"attribute.name.generic.armorToughness\": \"Armor Toughness\"\n  }\n}\n"
  },
  {
    "path": "fabric/build.gradle.kts",
    "content": "dependencies {\n    compileOnlyApi(projects.viabackwardsCommon)\n    compileOnly(libs.fabricLoader)\n    compileOnly(libs.log4j)\n}\n"
  },
  {
    "path": "fabric/src/main/java/com/viaversion/viabackwards/ViaFabricAddon.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.viaversion.viabackwards;\n\nimport com.viaversion.viabackwards.api.ViaBackwardsPlatform;\nimport com.viaversion.viabackwards.fabric.util.LoggerWrapper;\nimport java.io.File;\nimport java.nio.file.Path;\nimport java.util.logging.Logger;\nimport net.fabricmc.loader.api.FabricLoader;\nimport org.apache.logging.log4j.LogManager;\n\npublic class ViaFabricAddon implements ViaBackwardsPlatform, Runnable {\n    private final Logger logger = new LoggerWrapper(LogManager.getLogger(\"ViaBackwards\"));\n    private File configDir;\n\n    @Override\n    public void run() {\n        Path configDirPath = FabricLoader.getInstance().getConfigDir().resolve(\"ViaBackwards\");\n        configDir = configDirPath.toFile();\n        this.init(new File(getDataFolder(), \"config.yml\"));\n        this.enable();\n    }\n\n    @Override\n    public void disable() {\n        // Not possible\n    }\n\n    @Override\n    public File getDataFolder() {\n        return configDir;\n    }\n\n    @Override\n    public Logger getLogger() {\n        return logger;\n    }\n}\n"
  },
  {
    "path": "fabric/src/main/java/com/viaversion/viabackwards/fabric/util/LoggerWrapper.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.viaversion.viabackwards.fabric.util;\n\nimport java.text.MessageFormat;\nimport java.util.logging.Level;\nimport java.util.logging.LogRecord;\nimport java.util.logging.Logger;\n\npublic class LoggerWrapper extends Logger {\n    private final org.apache.logging.log4j.Logger base;\n\n    public LoggerWrapper(org.apache.logging.log4j.Logger logger) {\n        super(\"logger\", null);\n        this.base = logger;\n    }\n\n    public void log(LogRecord record) {\n        this.log(record.getLevel(), record.getMessage());\n    }\n\n    public void log(Level level, String msg) {\n        if (level == Level.FINE) {\n            this.base.debug(msg);\n        } else if (level == Level.WARNING) {\n            this.base.warn(msg);\n        } else if (level == Level.SEVERE) {\n            this.base.error(msg);\n        } else if (level == Level.INFO) {\n            this.base.info(msg);\n        } else {\n            this.base.trace(msg);\n        }\n\n    }\n\n    public void log(Level level, String msg, Object param1) {\n        if (level == Level.FINE) {\n            this.base.debug(msg, param1);\n        } else if (level == Level.WARNING) {\n            this.base.warn(msg, param1);\n        } else if (level == Level.SEVERE) {\n            this.base.error(msg, param1);\n        } else if (level == Level.INFO) {\n            this.base.info(msg, param1);\n        } else {\n            this.base.trace(msg, param1);\n        }\n\n    }\n\n    public void log(Level level, String msg, Object[] params) {\n        log(level, MessageFormat.format(msg, params));\n    }\n\n    public void log(Level level, String msg, Throwable params) {\n        if (level == Level.FINE) {\n            this.base.debug(msg, params);\n        } else if (level == Level.WARNING) {\n            this.base.warn(msg, params);\n        } else if (level == Level.SEVERE) {\n            this.base.error(msg, params);\n        } else if (level == Level.INFO) {\n            this.base.info(msg, params);\n        } else {\n            this.base.trace(msg, params);\n        }\n\n    }\n}\n"
  },
  {
    "path": "fabric/src/main/resources/fabric.mod.json",
    "content": "{\n  \"schemaVersion\": 1,\n  \"id\": \"viabackwards\",\n  \"name\": \"ViaBackwards\",\n  \"version\": \"${version}\",\n  \"description\": \"${description}\",\n  \"license\": \"GPL-3.0\",\n  \"contact\": {\n    \"homepage\": \"https://viaversion.com/backwards\",\n    \"issues\": \"https://github.com/ViaVersion/ViaBackwards/issues\",\n    \"sources\": \"https://github.com/ViaVersion/ViaBackwards\"\n  },\n  \"icon\": \"assets/viabackwards/textures/squarelogo.png\",\n  \"environment\": \"*\",\n  \"authors\": [\n    \"Matsv\",\n    \"kennytv\",\n    \"Gerrygames\",\n    \"creeper123123321\",\n    \"ForceUpdate1\",\n    \"EnZaXD\"\n  ],\n  \"entrypoints\": {\n    \"viafabric:via_api_initialized\": [\n      \"com.viaversion.viabackwards.ViaFabricAddon\"\n    ]\n  },\n  \"depends\": {\n    \"viafabric\": \">=0.4.14\"\n  },\n    \"custom\": {\n        \"modmenu:api\": true,\n        \"modmenu\": {\n            \"badges\": [ \"library\" ],\n            \"parent\": \"viafabric\"\n        }\n    }\n}\n"
  },
  {
    "path": "gradle/libs.versions.toml",
    "content": "metadata.format.version = \"1.1\"\n\n[versions]\n\n# ViaVersion\nviaver = \"5.9.0-SNAPSHOT\"\n\n# Common provided\nnetty = \"4.0.20.Final\"\nguava = \"17.0\"\nlog4j = \"2.8.1\"\n\ncheckerQual = \"3.53.1\"\n\n# Platforms\npaper = \"1.16.5-R0.1-SNAPSHOT\"\nvelocity = \"3.4.0\"\nfabricLoader = \"0.11.6\"\nviaProxy = \"[3.0.0,4.0.0)\"\n\n[libraries]\n\nviaver = { group = \"com.viaversion\", name = \"viaversion\", version.ref = \"viaver\" }\n\nnetty = { group = \"io.netty\", name = \"netty-all\", version.ref = \"netty\" }\nguava = { group = \"com.google.guava\", name = \"guava\", version.ref = \"guava\" }\nlog4j = { group = \"org.apache.logging.log4j\", name = \"log4j-api\", version.ref = \"log4j\" }\n\ncheckerQual = { group = \"org.checkerframework\", name = \"checker-qual\", version.ref = \"checkerQual\" }\n\npaper = { group = \"com.destroystokyo.paper\", name = \"paper-api\", version.ref = \"paper\" }\nvelocity = { group = \"com.velocitypowered\", name = \"velocity-api\", version.ref = \"velocity\" }\nfabricLoader = { group = \"net.fabricmc\", name = \"fabric-loader\", version.ref = \"fabricLoader\" }\nviaProxy = { group = \"net.raphimc\", name = \"ViaProxy\", version.ref = \"viaProxy\" }\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionSha256Sum=2ab2958f2a1e51120c326cad6f385153bb11ee93b3c216c5fccebfdfbb7ec6cb\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-9.4.1-bin.zip\nnetworkTimeout=10000\nvalidateDistributionUrl=true\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "gradle.properties",
    "content": "projectVersion=5.9.0-SNAPSHOT\n\n# Smile emoji (note that modrinth may not have added the version on release yet)\nmcVersions=26.1.1, 26.1, 1.21.11, 1.21.10, 1.21.9, 1.21.8, 1.21.7, 1.21.6, 1.21.5, 1.21.4, 1.21.3, 1.21.2, 1.21.1, 1.21, 1.20.6, 1.20.5, 1.20.4, 1.20.3, 1.20.2, 1.20.1, 1.20, 1.19.4, 1.19.3, 1.19.2, 1.19.1, 1.19, 1.18.2, 1.18.1, 1.18, 1.17.1, 1.17, 1.16.5, 1.16.4, 1.16.3, 1.16.2, 1.16.1, 1.16, 1.15.2, 1.15.1, 1.15, 1.14.4, 1.14.3, 1.14.2, 1.14.1, 1.14, 1.13.2, 1.13.1, 1.13, 1.12.2, 1.12.1, 1.12, 1.11.2, 1.11.1, 1.11, 1.10.2, 1.10.1, 1.10\nmcVersionRange=1.10-26.1.1\nvelocityVersion=3.4-3.5\n\norg.gradle.configureondemand=true\norg.gradle.parallel=true\norg.gradle.configuration-cache=true\n"
  },
  {
    "path": "gradlew",
    "content": "#!/bin/sh\n\n#\n# Copyright © 2015 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# SPDX-License-Identifier: Apache-2.0\n#\n\n##############################################################################\n#\n#   Gradle start up script for POSIX generated by Gradle.\n#\n#   Important for running:\n#\n#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is\n#       noncompliant, but you have some other compliant shell such as ksh or\n#       bash, then to run this script, type that shell name before the whole\n#       command line, like:\n#\n#           ksh Gradle\n#\n#       Busybox and similar reduced shells will NOT work, because this script\n#       requires all of these POSIX shell features:\n#         * functions;\n#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,\n#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;\n#         * compound commands having a testable exit status, especially «case»;\n#         * various built-in commands including «command», «set», and «ulimit».\n#\n#   Important for patching:\n#\n#   (2) This script targets any POSIX shell, so it avoids extensions provided\n#       by Bash, Ksh, etc; in particular arrays are avoided.\n#\n#       The \"traditional\" practice of packing multiple parameters into a\n#       space-separated string is a well documented source of bugs and security\n#       problems, so this is (mostly) avoided, by progressively accumulating\n#       options in \"$@\", and eventually passing that to Java.\n#\n#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,\n#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;\n#       see the in-line comments for details.\n#\n#       There are tweaks for specific operating systems such as AIX, CygWin,\n#       Darwin, MinGW, and NonStop.\n#\n#   (3) This script is generated from the Groovy template\n#       https://github.com/gradle/gradle/blob/2d6327017519d23b96af35865dc997fcb544fb40/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt\n#       within the Gradle project.\n#\n#       You can find Gradle at https://github.com/gradle/gradle/.\n#\n##############################################################################\n\n# Attempt to set APP_HOME\n\n# Resolve links: $0 may be a link\napp_path=$0\n\n# Need this for daisy-chained symlinks.\nwhile\n    APP_HOME=${app_path%\"${app_path##*/}\"}  # leaves a trailing /; empty if no leading path\n    [ -h \"$app_path\" ]\ndo\n    ls=$( ls -ld \"$app_path\" )\n    link=${ls#*' -> '}\n    case $link in             #(\n      /*)   app_path=$link ;; #(\n      *)    app_path=$APP_HOME$link ;;\n    esac\ndone\n\n# This is normally unused\n# shellcheck disable=SC2034\nAPP_BASE_NAME=${0##*/}\n# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)\nAPP_HOME=$( cd -P \"${APP_HOME:-./}\" > /dev/null && printf '%s\\n' \"$PWD\" ) || exit\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=maximum\n\nwarn () {\n    echo \"$*\"\n} >&2\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n} >&2\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"$( uname )\" in                #(\n  CYGWIN* )         cygwin=true  ;; #(\n  Darwin* )         darwin=true  ;; #(\n  MSYS* | MINGW* )  msys=true    ;; #(\n  NONSTOP* )        nonstop=true ;;\nesac\n\n\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=$JAVA_HOME/jre/sh/java\n    else\n        JAVACMD=$JAVA_HOME/bin/java\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=java\n    if ! command -v java >/dev/null 2>&1\n    then\n        die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nfi\n\n# Increase the maximum file descriptors if we can.\nif ! \"$cygwin\" && ! \"$darwin\" && ! \"$nonstop\" ; then\n    case $MAX_FD in #(\n      max*)\n        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        MAX_FD=$( ulimit -H -n ) ||\n            warn \"Could not query maximum file descriptor limit\"\n    esac\n    case $MAX_FD in  #(\n      '' | soft) :;; #(\n      *)\n        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        ulimit -n \"$MAX_FD\" ||\n            warn \"Could not set maximum file descriptor limit to $MAX_FD\"\n    esac\nfi\n\n# Collect all arguments for the java command, stacking in reverse order:\n#   * args from the command line\n#   * the main class name\n#   * -classpath\n#   * -D...appname settings\n#   * --module-path (only if needed)\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif \"$cygwin\" || \"$msys\" ; then\n    APP_HOME=$( cygpath --path --mixed \"$APP_HOME\" )\n\n    JAVACMD=$( cygpath --unix \"$JAVACMD\" )\n\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    for arg do\n        if\n            case $arg in                                #(\n              -*)   false ;;                            # don't mess with options #(\n              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath\n                    [ -e \"$t\" ] ;;                      #(\n              *)    false ;;\n            esac\n        then\n            arg=$( cygpath --path --ignore --mixed \"$arg\" )\n        fi\n        # Roll the args list around exactly as many times as the number of\n        # args, so each arg winds up back in the position where it started, but\n        # possibly modified.\n        #\n        # NB: a `for` loop captures its iteration list before it begins, so\n        # changing the positional parameters here affects neither the number of\n        # iterations, nor the values presented in `arg`.\n        shift                   # remove old arg\n        set -- \"$@\" \"$arg\"      # push replacement arg\n    done\nfi\n\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Collect all arguments for the java command:\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,\n#     and any embedded shellness will be escaped.\n#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be\n#     treated as '${Hostname}' itself on the command line.\n\nset -- \\\n        \"-Dorg.gradle.appname=$APP_BASE_NAME\" \\\n        -jar \"$APP_HOME/gradle/wrapper/gradle-wrapper.jar\" \\\n        \"$@\"\n\n# Stop when \"xargs\" is not available.\nif ! command -v xargs >/dev/null 2>&1\nthen\n    die \"xargs is not available\"\nfi\n\n# Use \"xargs\" to parse quoted args.\n#\n# With -n1 it outputs one arg per line, with the quotes and backslashes removed.\n#\n# In Bash we could simply go:\n#\n#   readarray ARGS < <( xargs -n1 <<<\"$var\" ) &&\n#   set -- \"${ARGS[@]}\" \"$@\"\n#\n# but POSIX shell has neither arrays nor command substitution, so instead we\n# post-process each arg (as a line of input to sed) to backslash-escape any\n# character that might be a shell metacharacter, then use eval to reverse\n# that process (while maintaining the separation between arguments), and wrap\n# the whole thing up as a single \"set\" statement.\n#\n# This will of course break if any of these variables contains a newline or\n# an unmatched quote.\n#\n\neval \"set -- $(\n        printf '%s\\n' \"$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\" |\n        xargs -n1 |\n        sed ' s~[^-[:alnum:]+,./:=@_]~\\\\&~g; ' |\n        tr '\\n' ' '\n    )\" '\"$@\"'\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\r\n@rem you may not use this file except in compliance with the License.\r\n@rem You may obtain a copy of the License at\r\n@rem\r\n@rem      https://www.apache.org/licenses/LICENSE-2.0\r\n@rem\r\n@rem Unless required by applicable law or agreed to in writing, software\r\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\r\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n@rem See the License for the specific language governing permissions and\r\n@rem limitations under the License.\r\n@rem\r\n@rem SPDX-License-Identifier: Apache-2.0\r\n@rem\r\n\r\n@if \"%DEBUG%\"==\"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\"==\"\" set DIRNAME=.\r\n@rem This is normally unused\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\r\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif %ERRORLEVEL% equ 0 goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\n\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -jar \"%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\" %*\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif %ERRORLEVEL% equ 0 goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nset EXIT_CODE=%ERRORLEVEL%\r\nif %EXIT_CODE% equ 0 set EXIT_CODE=1\r\nif not \"\"==\"%GRADLE_EXIT_CONSOLE%\" exit %EXIT_CODE%\r\nexit /b %EXIT_CODE%\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "settings.gradle.kts",
    "content": "enableFeaturePreview(\"TYPESAFE_PROJECT_ACCESSORS\")\n\ndependencyResolutionManagement {\n    repositories {\n        mavenLocal()\n        maven(\"https://repo.viaversion.com\")\n        maven(\"https://repo.papermc.io/repository/maven-public/\")\n        mavenCentral()\n    }\n    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)\n}\n\npluginManagement {\n    plugins {\n        id(\"com.gradleup.shadow\") version \"9.4.1\"\n        id(\"net.kyori.blossom\") version \"2.2.0\"\n        id(\"org.jetbrains.gradle.plugin.idea-ext\") version \"1.4.1\"\n\n        // A nice no-conflict comment for patching in downgrading\n    }\n}\n\nplugins {\n    id(\"org.gradle.toolchains.foojay-resolver-convention\") version \"1.0.0\"\n}\n\nrootProject.name = \"viabackwards-parent\"\n\nincludeBuild(\"build-logic\")\n\nsetupViaSubproject(\"common\")\nsetupViaSubproject(\"bukkit\")\nsetupViaSubproject(\"velocity\")\nsetupViaSubproject(\"sponge\")\nsetupViaSubproject(\"fabric\")\n\nsetupSubproject(\"viabackwards\") {\n    projectDir = file(\"universal\")\n}\n\nfun setupViaSubproject(name: String) {\n    setupSubproject(\"viabackwards-$name\") {\n        projectDir = file(name)\n    }\n}\n\ninline fun setupSubproject(name: String, block: ProjectDescriptor.() -> Unit) {\n    include(name)\n    project(\":$name\").apply(block)\n}\n"
  },
  {
    "path": "sponge/build.gradle.kts",
    "content": ""
  },
  {
    "path": "sponge/src/main/resources/META-INF/sponge_plugins.json",
    "content": "{\n  \"loader\": {\n    \"name\": \"java_plain\",\n    \"version\": \"1.0\"\n  },\n  \"license\": \"GNU GPLv3\",\n  \"global\": {\n    \"version\": \"${version}\",\n    \"links\": {\n      \"source\": \"https://github.com/ViaVersion/ViaBackwards\",\n      \"issues\": \"https://github.com/ViaVersion/ViaBackwards/issues\"\n    },\n    \"contributors\": [\n      {\n        \"name\": \"Matsv\",\n        \"description\": \"Maintainer\"\n      },\n      {\n        \"name\": \"kennytv\",\n        \"description\": \"Maintainer\"\n      },\n      {\n        \"name\": \"Gerrygames\",\n        \"description\": \"Contributor\"\n      },\n      {\n        \"name\": \"creeper123123321\",\n        \"description\": \"Contributor\"\n      },\n      {\n        \"name\": \"ForceUpdate1\",\n        \"description\": \"Contributor\"\n      },\n      {\n        \"name\": \"EnZaXD\",\n        \"description\": \"Maintainer\"\n      }\n    ],\n    \"dependencies\": [\n      {\n        \"id\": \"spongeapi\",\n        \"version\": \"8.0.0\"\n      },\n      {\n        \"id\": \"viasponge\",\n        \"version\": \"1.1.0\"\n      },\n      {\n        \"id\": \"viaversion\",\n        \"version\": \"[5.8.1-SNAPSHOT,)\"\n      }\n    ]\n  },\n  \"plugins\": [\n    {\n      \"id\": \"viabackwards\",\n      \"name\": \"ViaBackwards\",\n      \"entrypoint\": \"com.viaversion.sponge.util.DummyEntrypoint\",\n      \"description\": \"${description}\"\n    }\n  ]\n}\n"
  },
  {
    "path": "universal/build.gradle.kts",
    "content": "plugins {\n    id(\"io.papermc.hangar-publish-plugin\") version \"0.1.4\"\n    id(\"com.modrinth.minotaur\") version \"2.+\"\n\n    // A nice no-conflict comment for patching in downgrading\n}\n\ndependencies {\n    api(projects.viabackwardsCommon)\n    api(projects.viabackwardsBukkit)\n    api(projects.viabackwardsVelocity)\n    api(projects.viabackwardsFabric)\n    api(projects.viabackwardsSponge)\n}\n\ntasks {\n    shadowJar {\n        manifest {\n            attributes[\"paperweight-mappings-namespace\"] = \"mojang\"\n        }\n        archiveFileName.set(\"ViaBackwards-${project.version}.jar\")\n        destinationDirectory.set(rootProject.projectDir.resolve(\"build/libs\"))\n    }\n}\n\nval branch = rootProject.branchName()\nval baseVersion = project.version as String\nval isRelease = !baseVersion.contains('-')\nval isMainBranch = branch == \"master\"\nif (!isRelease || isMainBranch) { // Only publish releases from the main branch\n    val suffixedVersion = if (isRelease) baseVersion else baseVersion + \"+\" + System.getenv(\"GITHUB_RUN_NUMBER\")\n    val changelogContent = if (isRelease) {\n        \"See [GitHub](https://github.com/ViaVersion/ViaBackwards) for release notes.\"\n    } else {\n        val commitHash = rootProject.latestCommitHash()\n        \"[$commitHash](https://github.com/ViaVersion/ViaBackwards/commit/$commitHash) ${rootProject.latestCommitMessage()}\"\n    }\n    modrinth {\n        // val snapshotVersion = rootProject.parseMinecraftSnapshotVersion(project.version as String)\n        val mcVersions: List<String> = (property(\"mcVersions\") as String)\n            .split(\",\")\n            .map { it.trim() }\n        //.let { if (snapshotVersion != null) it + snapshotVersion else it } // We're usually too fast for modrinth\n\n        token.set(System.getenv(\"MODRINTH_TOKEN\"))\n        projectId.set(\"viabackwards\")\n        versionType.set(if (isRelease) \"release\" else if (isMainBranch) \"beta\" else \"alpha\")\n        versionNumber.set(suffixedVersion)\n        versionName.set(suffixedVersion)\n        changelog.set(changelogContent)\n        uploadFile.set(tasks.shadowJar.flatMap { it.archiveFile })\n        gameVersions.set(mcVersions)\n        loaders.add(\"fabric\")\n        loaders.add(\"paper\")\n        loaders.add(\"folia\")\n        loaders.add(\"velocity\")\n        autoAddDependsOn.set(false)\n        detectLoaders.set(false)\n        dependencies {\n            required.project(\"viaversion\")\n            optional.project(\"viafabric\")\n            optional.project(\"viarewind\")\n        }\n    }\n    tasks.modrinth {\n        notCompatibleWithConfigurationCache(\"\")\n    }\n\n    hangarPublish {\n        publications.register(\"plugin\") {\n            version = suffixedVersion\n            id = \"ViaBackwards\"\n            channel = if (isRelease) \"Release\" else if (isMainBranch) \"Snapshot\" else \"Alpha\"\n            changelog = changelogContent\n            apiKey = System.getenv(\"HANGAR_TOKEN\")\n            platforms {\n                paper {\n                    jar = tasks.shadowJar.flatMap { it.archiveFile }\n                    platformVersions = listOf(property(\"mcVersionRange\") as String)\n                    dependencies {\n                        hangar(\"ViaVersion\") {\n                            required = true\n                        }\n                        hangar(\"ViaRewind\") {\n                            required = false\n                        }\n                    }\n                }\n                velocity {\n                    jar = tasks.shadowJar.flatMap { it.archiveFile }\n                    platformVersions = listOf(property(\"velocityVersion\") as String)\n                    dependencies {\n                        hangar(\"ViaVersion\") {\n                            required = true\n                        }\n                        hangar(\"ViaRewind\") {\n                            required = false\n                        }\n                    }\n                }\n            }\n        }\n    }\n    tasks.named(\"publishPluginPublicationToHangar\") {\n        notCompatibleWithConfigurationCache(\"\")\n    }\n}\n"
  },
  {
    "path": "velocity/build.gradle.kts",
    "content": "dependencies {\n    compileOnlyApi(projects.viabackwardsCommon)\n    compileOnly(libs.velocity) {\n        // Requires Java 17\n        exclude(\"com.velocitypowered\", \"velocity-brigadier\")\n    }\n    annotationProcessor(libs.velocity)\n}\n"
  },
  {
    "path": "velocity/src/main/java/com/viaversion/viabackwards/VelocityPlugin.java",
    "content": "/*\n * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards\n * Copyright (C) 2016-2026 ViaVersion and contributors\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.viaversion.viabackwards;\n\nimport com.google.inject.Inject;\nimport com.velocitypowered.api.event.PostOrder;\nimport com.velocitypowered.api.event.Subscribe;\nimport com.velocitypowered.api.event.proxy.ProxyInitializeEvent;\nimport com.velocitypowered.api.plugin.Dependency;\nimport com.velocitypowered.api.plugin.Plugin;\nimport com.velocitypowered.api.plugin.annotation.DataDirectory;\nimport com.viaversion.viabackwards.api.ViaBackwardsPlatform;\nimport com.viaversion.viabackwards.utils.VersionInfo;\nimport com.viaversion.viaversion.api.Via;\nimport com.viaversion.viaversion.velocity.util.LoggerWrapper;\nimport java.io.File;\nimport java.nio.file.Path;\nimport java.util.logging.Logger;\n\n@Plugin(id = \"viabackwards\",\n    name = \"ViaBackwards\",\n    version = VersionInfo.VERSION,\n    authors = {\"Matsv\", \"kennytv\", \"Gerrygames\", \"creeper123123321\", \"ForceUpdate1\", \"EnZaXD\"},\n    description = \"Allows the connection of older clients to newer server versions for Minecraft servers.\",\n    dependencies = {@Dependency(id = \"viaversion\")}\n)\npublic class VelocityPlugin implements ViaBackwardsPlatform {\n    private Logger logger;\n    @Inject\n    private org.slf4j.Logger loggerSlf4j;\n    @Inject\n    @DataDirectory\n    private Path configPath;\n\n    @SuppressWarnings(\"deprecation\")\n    @Subscribe(order = PostOrder.LATE)\n    public void onProxyStart(ProxyInitializeEvent event) {\n        this.logger = new LoggerWrapper(loggerSlf4j);\n        Via.getManager().addEnableListener(() -> this.init(new File(getDataFolder(), \"config.yml\")));\n        Via.getManager().addPostEnableListener(this::enable);\n    }\n\n    @Override\n    public void disable() {\n        // Not possible\n    }\n\n    @Override\n    public File getDataFolder() {\n        return configPath.toFile();\n    }\n\n    @Override\n    public Logger getLogger() {\n        return logger;\n    }\n}\n"
  }
]