[
  {
    "path": ".github/ISSUE_TEMPLATE/bug-report.yml",
    "content": "name: Bug Report\ndescription: Report a bug / crash\nlabels: [bug]\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        To help us help you, please provide as much information as you can, this helps us with debugging and reproducting the issue.\n  - type: input\n    id: bg-version\n    attributes:\n      label: Building Gadgets version\n      description: |\n        You can find the version of the mod in the mods file name or in-game in the mods button on the main menu.\n      placeholder: 3.4.0\n    validations:\n      required: true\n  - type: input\n    id: mc-version\n    attributes:\n      label: Minecraft Version\n      placeholder: 1.16.5\n    validations:\n      required: true\n  - type: input\n    id: forge-version\n    attributes:\n      label: Forge Version\n      description: |\n        You can get this from the main menu screen in the bottom left corner of the screen.\n      placeholder: 36.1.24\n    validations:\n      required: false\n  - type: input\n    id: modpack\n    attributes:\n      label: Modpack & Version\n      description: |\n        If you are running a Modpack, please provide the name and version of the pack.\n      placeholder: Direwolf20 - 1.16 (2.4)\n    validations:\n      required: false\n  - type: dropdown\n    id: optifine\n    validations:\n        required: true\n    attributes:\n      label: Do you have optifine installed?\n      description: |\n        If you have a different rendering mod installed, please add to in the extra info box at the end.\n      multiple: false\n      options:\n        - 'Yes'\n        - 'No'\n        - 'I have other rendering based mods installed'\n  - type: input\n    id: bugdesc\n    attributes:\n      label: Describe the issue\n      description: |\n        A clear and concise description of what the bug is\n    validations:\n      required: true\n  - type: textarea\n    id: reproduce\n    attributes:\n      label: Steps to reproduce\n      description: Tell us how we can reproduce the issue\n      placeholder: |\n        1. Open Mc...\n        2. Get gadget...\n        3. See error...\n    validations:\n      required: true\n  - type: input\n    id: expectedBehaviour\n    attributes:\n      label: Expected behaviour\n      description: |\n        A clear and concise description of what you expected to happen.\n    validations:\n      required: true\n  - type: textarea\n    id: screenshots\n    attributes:\n      label: Screenshots\n      description: If applicable, add screenshots to help explain your problem.\n    validations:\n      required: false\n  - type: textarea\n    id: logs\n    attributes:\n      label: Log files\n      description: \"Link(s) to any log files that you can provide, typically, /logs/debug.log and /logs/latest.log\"\n      placeholder: |\n        https://pste.ch/ihaveaproble\n        https://pastebin.com/\n        https://paste.feed-the-beast.com/\n    validations:\n      required: false\n  - type: textarea\n    id: additional\n    attributes:\n      label: Additional information\n      description: Add any other context about the problem here\n    validations:\n      required: false\n  - type: checkboxes\n    id: terms\n    attributes:\n      label: Information\n      options:\n        - label: I have provided as much information as possible\n          required: true\n\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature-request.md",
    "content": "---\nname: Feature Request\nabout: Suggest an idea/feature/enhancement\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**IMPORTANT:** Before continuing, please ensure that Building Gadgets (and any other mods involved) is updated to the latest available version\n----------------------------------------------------------------------------\nAfter deleting this template, please provide the following information:\n* A detailed explanation of the suggestion\n"
  },
  {
    "path": ".github/workflows/gradle.yml",
    "content": "name: Build & Create artifact\n\non:\n  push:\n    branches: [ master ]\n  pull_request:\n    branches: [ master ]\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v3\n      - name: Set up JDK  17\n        uses: actions/setup-java@v2\n        with:\n          java-version: '17'\n          distribution: 'temurin'\n      - uses: actions/cache@v3\n        with:\n          path: |\n            ~/.gradle/caches\n            ~/.gradle/wrapper\n          key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}\n          restore-keys: |\n            ${{ runner.os }}-gradle-\n      - name: Cleanup Gradle Cache\n        run: |\n          rm -f ~/.gradle/caches/modules-2/modules-2.lock\n          rm -f ~/.gradle/caches/modules-2/gc.properties\n      - name: Build with Gradle\n        run: |\n          chmod +x ./gradlew\n          ./gradlew build --no-daemon\n      - name: Setting mod version\n        run: |\n          cat $GITHUB_WORKSPACE/gradle.properties | grep ^version= >> $GITHUB_ENV\n          cat $GITHUB_WORKSPACE/gradle.properties | grep ^minecraft_version= >> $GITHUB_ENV\n      - name: Create package name\n        run: echo \"package_name=BuildingGadgets-$minecraft_version-$version-${{ github.head_ref || 'main' }}-SNAPSHOT-$GITHUB_RUN_NUMBER\" >> $GITHUB_ENV\n      - uses: actions/upload-artifact@v2\n        with:\n          name: ${{ env.package_name }}\n          path: build/libs\n"
  },
  {
    "path": ".github/workflows/release.yml",
    "content": "name: Auto Release\n\non:\n  push:\n    tags:\n      - 'release/*'\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v3\n      - name: Set up JDK  17\n        uses: actions/setup-java@v2\n        with:\n          java-version: '17'\n          distribution: 'temurin'\n      - uses: actions/cache@v3\n        with:\n          path: |\n            ~/.gradle/caches\n            ~/.gradle/wrapper\n          key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}\n          restore-keys: |\n            ${{ runner.os }}-gradle-\n      - name: Cleanup Gradle Cache\n        run: |\n          rm -f ~/.gradle/caches/modules-2/modules-2.lock\n          rm -f ~/.gradle/caches/modules-2/gc.properties\n      - name: Build & Publish to Github Maven\n        run: |\n          chmod +x ./gradlew\n          ./gradlew build publish --no-daemon\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n      - name: Release\n        env:\n          CURSE_TOKEN: ${{ secrets.CURSE_TOKEN }}\n        run: ./gradlew curseforge --no-daemon\n      - name: Release to Github\n        uses: softprops/action-gh-release@v1\n        with:\n          generate_release_notes: true\n          body_path: ./CHANGELOG.md\n          files: build/libs/*.jar\n"
  },
  {
    "path": ".github/workflows/stale.yml",
    "content": "name: 'Close stale issues and PRs'\non:\n  schedule:\n    - cron: '30 1 * * *'\n\npermissions:\n  issues: write\n  pull-requests: write\n\njobs:\n  stale:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/stale@v9\n        with:\n          stale-issue-message: 'This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.'\n          close-issue-message: 'This issue / Pull request has been closed as there has been no recent activity after being marked as stale.'\n          days-before-stale: 30\n          days-before-close: 5\n          days-before-pr-close: -1\n          stale-issue-label: \"stale\"\n          only-labels: \"waiting on response\"\n          exempt-all-assignees: true\n          exempt-all-milestones: true\n"
  },
  {
    "path": ".gitignore",
    "content": "/*\n\n# folders\n!/gradle\n!/src\n\n# files\n!/.gitignore\n!/.github\n!/README.md\n!/CONTRIBUTION.md\n!/License.md\n!/build.gradle\n!/settings.gradle\n!/gradlew\n!/gradlew.bat\n!/gradle.properties\n!/update.json\n!/CHANGELOG-OLD.md\n!/SPEC.md\n!/EclipseCodeStyle.xml\n!/IntelliJIdeaCodeStyle.xml\n\n.DS_Store\n"
  },
  {
    "path": "README.md",
    "content": "## Important notice\n\nAs of Minecraft `1.20.1` this version of Building Gadget (Building Gadgets 1) is officially the 'Legacy' version of Building Gadgets. I will continue to update the mod for the Minecraft versions we already support but this mod will not be ported to Minecraft `1.20.1` or higher. \n\nGo and download [`Building Gadgets 2`](https://github.com/direwolf20-mc/buildinggadgets2)! It's the same mod but with a bunch of new and awesome features built on a brand-new codebase. \n\n---\n<p align=\"center\" style=\"padding: 3em;\"><img width=\"60\" src=\"https://github.com/Direwolf20-MC/BuildingGadgets/blob/master/src/main/resources/buildinggadgets_logo.png?raw=true\" /></p>\n<h1 align=\"center\" style=\"margin-top: 20px; border-bottom: 0;\">Building Gadgets (1.12.2 - 1.19.4)</h1>\n<p align=\"center\">\n<b>Update!</b> See <a href=\"https://github.com/direwolf20-mc/buildinggadgets2\">Building Gadgets 2</a> for Minecraft <code>1.20.1</code> and higher!\n</p>\n<p align=\"center\">Sometimes, building large structures can be a little tedious, and take a lot of effort. Not all of us are great builders you know!\n</p>\n<p align=\"center\">\n    <a href=\"https://minecraft.curseforge.com/projects/building-gadgets\"><img src=\"http://cf.way2muchnoise.eu/full_298187_downloads.svg\" /></a>\n    <a href=\"https://minecraft.curseforge.com/projects/building-gadgets\"><img src=\"http://cf.way2muchnoise.eu/versions/298187.svg\" /></a>\n    <a href=\"https://github.com/Direwolf20-MC/BuildingGadgets/blob/master/LICENSE.md\"><img alt=\"GitHub license\" src=\"https://img.shields.io/github/license/direwolf20-MC/buildinggadgets\"></a>\n    <a href=\"https://discord.gg/qqUUtu3\"><img alt=\"discord\" src=\"https://img.shields.io/discord/541740767761399808\" ></a>\n</p>\n\n<p data-comment=\"this fakes a line break\">&zwnj;</p>\n\n# Welcome to Dire's Building Gadgets!\n\nDire's `Building Gadgets` aims to make building a little bit easier. At this time there are `four` tools, and they are described below. Alternatively, watch the following mod spotlight for instructions!\n\n[![Youtube Thumbnail](https://img.youtube.com/vi/D4Ib4h7aTSk/maxresdefault.jpg)](https://www.youtube.com/watch?v=D4Ib4h7aTSk)\n_(I'm a Youtube Link, click me)_\n\n## Usage\n\nTo see how to use Dire's `Building Gadgets` refer to our [Wiki](https://github.com/Direwolf20-MC/BuildingGadgets/wiki) which has been recently updated to be inline with the mod as of `1.16.5`. Please feel free to make feature requests to add more info to our wiki. We're always trying to improve.\n\nDirewolf20's Spotlights - [Spotlight Part One](https://youtu.be/D4Ib4h7aTSk) and [Spotlight Part Two](https://youtu.be/JS1Xx_kwQQ0)\n\n### Using the Gadgets\n\nBuilding gadgets has grown a lot since its initial release to the point that we have multiple tools for every occasion, Our wiki has a great set of instructions on how to use each of our Gadgets, check out the\n\n- ![](./src/main/resources/assets/buildinggadgets/textures/item/gadget_building.png) [Building Gadget](https://github.com/Direwolf20-MC/BuildingGadgets/wiki/Building-Gadget)\n- ![](./src/main/resources/assets/buildinggadgets/textures/item/gadget_exchanging.png) [Exchanger Gadget](https://github.com/Direwolf20-MC/BuildingGadgets/wiki/Exchanger-Gadget)\n- ![](./src/main/resources/assets/buildinggadgets/textures/item/gadget_copy_paste.png) [Copy Paste Gadget](https://github.com/Direwolf20-MC/BuildingGadgets/wiki/Copy-Paste-Gadget)\n- ![](./src/main/resources/assets/buildinggadgets/textures/item/gadget_destruction.png) [Destruction Gadget](https://github.com/Direwolf20-MC/BuildingGadgets/wiki/Destruction-Gadget)\n\n## Schematics / Templates\n\nBuilding Gadgets' copy & paste tool supports loading and saving templates via the `Template Manager` block. You can use this feature to save your builds and share them with others. \n\nIf you're looking for templates to use, check out the [r/9x9](https://www.reddit.com/r/9x9/) community on reddit 🎉\n\n## Contributing\n\nSee [Important Notice](#important-notice). Please support the new version of Building Gadgets at https://github.com/direwolf20-mc/buildinggadgets2 if you would like to support the project! You can see help maintain the legacy version by submitting PR's to this repo.\n\n## Credit\n\nAn especially big thanks to all of our [contributors](https://github.com/Direwolf20-MC/BuildingGadgets/graphs/contributors) for the continued support, bug reports (issues) and PR's. All of the [Forge Guys](https://github.com/orgs/MinecraftForge/people) and the creators of Minecraft (*Duh*)\n\n[License MIT](License.md)\n"
  },
  {
    "path": "build.gradle",
    "content": "plugins {\n    id 'eclipse'\n    id 'maven-publish'\n    id 'net.minecraftforge.gradle' version '5.1.+'\n    id \"com.matthewprenger.cursegradle\" version \"1.4.0\"\n}\n\napply {\n    // \"Simple\" fix for M1 (Apple Silicon) macs in the dev environment... All thanks to @mezz\n    from \"https://raw.githubusercontent.com/mezz/JustEnoughItems/1.18/Forge/buildtools/AppleSiliconSupport.gradle\"\n}\n\ndef ENV = System.getenv()\n\nversion = \"${version}-build.${ENV.GITHUB_RUN_NUMBER ?: 9999}+mc${minecraft_version}\"\ngroup = 'com.direwolf20'\narchivesBaseName = \"buildinggadgets\"\n\ndef forge_major = forge_version.tokenize('.')[0]\n\njava.toolchain.languageVersion = JavaLanguageVersion.of(17)\n\nminecraft {\n    mappings channel: 'official', version: \"${minecraft_version}\"\n    // makeObfSourceJar = false // an Srg named sources jar is made by default. uncomment this to disable.\n\n    accessTransformer = file('src/main/resources/META-INF/accesstransformer.cfg')\n\n    runs {\n        client {\n            workingDirectory project.file('run')\n            property 'forge.logging.markers', 'SCAN,REGISTRIES,REGISTRYDUMP'\n            property 'forge.logging.console.level', 'debug'\n            mods {\n                buildinggadgets {\n                    source sourceSets.main\n                }\n            }\n        }\n\n        server {\n            workingDirectory project.file('run')\n            property 'forge.logging.markers', 'SCAN,REGISTRIES,REGISTRYDUMP'\n            property 'forge.logging.console.level', 'debug'\n            mods {\n                buildinggadgets {\n                    source sourceSets.main\n                }\n            }\n        }\n\n        data {\n            workingDirectory project.file('run')\n            property 'forge.logging.markers', 'SCAN,REGISTRIES,REGISTRYDUMP'\n            property 'forge.logging.console.level', 'debug'\n            args '--mod', 'buildinggadgets', '--all', '--output', file('src/generated/resources/')\n            mods {\n                buildinggadgets {\n                    source sourceSets.main\n                }\n            }\n        }\n    }\n}\n\nsourceSets.main.resources { srcDir 'src/generated/resources' }\n\nrepositories {\n    maven {\n        url = \"https://modmaven.dev/\"\n    }\n}\n\ndependencies {\n    minecraft \"net.minecraftforge:forge:${minecraft_version}-${forge_version}\"\n\n    compileOnly fg.deobf(\"mezz.jei:jei-${minecraft_version}-common-api:${jei_version}\")\n    compileOnly fg.deobf(\"mezz.jei:jei-${minecraft_version}-forge-api:${jei_version}\")\n    runtimeOnly fg.deobf(\"mezz.jei:jei-${minecraft_version}-forge:${jei_version}\")\n}\n\nprocessResources {\n    // This might be the wrong way of doing it but basically this file doesn't always get updated\n    // for some reason so I'm deleting it before it's generated again\n    def path = new File(\"${project.buildDir}/resources/main/META-INF/mods.toml\")\n    if (path.exists()) {\n        path.delete()\n    }\n\n    exclude '.cache'\n    inputs.property \"version\", project.version\n\n    filesMatching(\"META-INF/mods.toml\") {\n        expand \"version\": project.version,\n                \"forgeversion\": project.forge_version,\n                \"forgeshortversion\": project.forge_version.split(\"\\\\.\")[0],\n                \"mcversion\": project.minecraft_version\n    }\n}\n\njar {\n    finalizedBy 'reobfJar'\n\n    manifest {\n        attributes([\"Specification-Title\"     : \"BuildingGadgets\",\n                    \"Specification-Vendor\"    : \"Direwolf20\",\n                    \"Specification-Version\"   : forge_major, // We are version 1 of the modlauncher specification\n                    \"Implementation-Title\"    : project.archivesBaseName,\n                    \"Implementation-Version\"  : project.version,\n                    \"Implementation-Vendor\"   : \"Direwolf20\",\n                    \"Implementation-Timestamp\": new Date().format(\"yyyy-MM-dd'T'HH:mm:ssZ\")])\n    }\n}\njava {\n    withSourcesJar()\n}\n\ntask deobfJar(type: Jar) {\n    from sourceSets.main.output\n    classifier 'deobf'\n}\n\nartifacts {\n    archives deobfJar\n}\n\npublishing {\n    publications {\n        maven(MavenPublication) {\n            from components.java\n            artifactId = project.archivesBaseName.toLowerCase()\n            artifact(deobfJar) {\n                classifier 'deobf'\n            }\n        }\n    }\n\n    repositories {\n        maven {\n            name = \"GitHubPackages\"\n            url = \"https://maven.pkg.github.com/Direwolf20-MC/BuildingGadgets\"\n            credentials {\n                username = ENV.GITHUB_ACTOR\n                password = ENV.GITHUB_TOKEN\n            }\n        }\n    }\n}\n\nif (ENV.CURSE_TOKEN) {\n    curseforge {\n        apiKey = ENV.CURSE_TOKEN\n        project {\n            id = \"298187\"\n            releaseType = \"release\"\n            addGameVersion \"${minecraft_version}\"\n            addGameVersion \"Forge\"\n            addGameVersion \"Java 17\"\n            mainArtifact(jar) {\n                relations {\n                    optionalDependency 'charging-gadgets'\n                }\n            }\n            changelog = file('CHANGELOG.md')\n            changelogType = 'markdown'\n        }\n    }\n}\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-7.5-bin.zip\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "gradle.properties",
    "content": "org.gradle.jvmargs=-Xmx2G\norg.gradle.daemon=false\nminecraft_version=1.19.4\nforge_version=45.2.15\nversion=3.18.0\njei_version=13.1.0.16\n"
  },
  {
    "path": "gradlew",
    "content": "#!/usr/bin/env sh\n\n#\n# Copyright 2015 the original author or 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\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >/dev/null\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >/dev/null\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\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# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn () {\n    echo \"$*\"\n}\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MSYS* | MINGW* )\n    msys=true\n    ;;\n  NONSTOP* )\n    nonstop=true\n    ;;\nesac\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\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    which java >/dev/null 2>&1 || 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.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" -a \"$nonstop\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif [ \"$cygwin\" = \"true\" -o \"$msys\" = \"true\" ] ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n\n    JAVACMD=`cygpath --unix \"$JAVACMD\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=`expr $i + 1`\n    done\n    case $i in\n        0) set -- ;;\n        1) set -- \"$args0\" ;;\n        2) set -- \"$args0\" \"$args1\" ;;\n        3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Escape application args\nsave () {\n    for i do printf %s\\\\n \"$i\" | sed \"s/'/'\\\\\\\\''/g;1s/^/'/;\\$s/\\$/' \\\\\\\\/\" ; done\n    echo \" \"\n}\nAPP_ARGS=`save \"$@\"`\n\n# Collect all arguments for the java command, following the shell quoting and substitution rules\neval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS \"\\\"-Dorg.gradle.appname=$APP_BASE_NAME\\\"\" -classpath \"\\\"$CLASSPATH\\\"\" org.gradle.wrapper.GradleWrapperMain \"$APP_ARGS\"\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@rem\n@rem Copyright 2015 the original author or authors.\n@rem\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\n@rem you may not use this file except in compliance with the License.\n@rem You may obtain a copy of the License at\n@rem\n@rem      https://www.apache.org/licenses/LICENSE-2.0\n@rem\n@rem Unless required by applicable law or agreed to in writing, software\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n@rem See the License for the specific language governing permissions and\n@rem limitations under the License.\n@rem\n\n@if \"%DEBUG%\" == \"\" @echo off\n@rem ##########################################################################\n@rem\n@rem  Gradle startup script for Windows\n@rem\n@rem ##########################################################################\n\n@rem Set local scope for the variables with windows NT shell\nif \"%OS%\"==\"Windows_NT\" setlocal\n\nset DIRNAME=%~dp0\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\nset APP_BASE_NAME=%~n0\nset APP_HOME=%DIRNAME%\n\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\n\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\n\n@rem Find java.exe\nif defined JAVA_HOME goto findJavaFromJavaHome\n\nset JAVA_EXE=java.exe\n%JAVA_EXE% -version >NUL 2>&1\nif \"%ERRORLEVEL%\" == \"0\" goto execute\n\necho.\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:findJavaFromJavaHome\nset JAVA_HOME=%JAVA_HOME:\"=%\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\n\nif exist \"%JAVA_EXE%\" goto execute\n\necho.\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:execute\n@rem Setup the command line\n\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\n\n\n@rem Execute Gradle\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %*\n\n:end\n@rem End local scope for the variables with windows NT shell\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\n\n:fail\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\nrem the _cmd.exe /c_ return code!\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\nexit /b 1\n\n:mainEnd\nif \"%OS%\"==\"Windows_NT\" endlocal\n\n:omega\n"
  },
  {
    "path": "settings.gradle",
    "content": "pluginManagement {\n    repositories {\n        gradlePluginPortal()\n        maven { url = 'https://maven.minecraftforge.net/' }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/client/BuildingGadgetsJEI.java",
    "content": "package com.direwolf20.buildinggadgets.client;\n\nimport com.direwolf20.buildinggadgets.client.screen.TemplateManagerGUI;\nimport com.direwolf20.buildinggadgets.common.items.AbstractGadget;\nimport com.direwolf20.buildinggadgets.common.items.OurItems;\nimport com.direwolf20.buildinggadgets.common.util.ref.Reference;\nimport mezz.jei.api.JeiPlugin;\nimport mezz.jei.api.constants.VanillaTypes;\nimport mezz.jei.api.gui.handlers.IGuiContainerHandler;\nimport mezz.jei.api.registration.IGuiHandlerRegistration;\nimport mezz.jei.api.registration.ISubtypeRegistration;\nimport net.minecraft.client.renderer.Rect2i;\nimport net.minecraft.resources.ResourceLocation;\nimport net.minecraft.world.item.Item;\nimport mezz.jei.api.IModPlugin;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\n@JeiPlugin\npublic class BuildingGadgetsJEI implements IModPlugin {\n    @Override\n    public ResourceLocation getPluginUid() {\n\n        return new ResourceLocation(Reference.MODID, \"jei_plugin\");\n    }\n\n    @Override\n    public void registerGuiHandlers(IGuiHandlerRegistration registration) {\n        registration.addGuiContainerHandler(TemplateManagerGUI.class, new GuiContainerHandler());\n    }\n\n    @Override\n    public void registerItemSubtypes(ISubtypeRegistration registration) {\n        List<Item> gadgets = new ArrayList<>() {{\n            add(OurItems.BUILDING_GADGET_ITEM.get());\n            add(OurItems.EXCHANGING_GADGET_ITEM.get());\n            add(OurItems.DESTRUCTION_GADGET_ITEM.get());\n            add(OurItems.COPY_PASTE_GADGET_ITEM.get());\n        }};\n\n        for (Item gadget : gadgets) {\n            registration.registerSubtypeInterpreter(VanillaTypes.ITEM_STACK, gadget, (ingredient, uidContext) -> {\n                if (!(ingredient.getItem() instanceof AbstractGadget))\n                    return \"\";\n\n                double energy = ingredient.getOrCreateTag().getDouble(\"energy\");\n                if (energy == 0)\n                    return \"empty\";\n                else if (energy == ((AbstractGadget) ingredient.getItem()).getEnergyMax())\n                    return \"charged\";\n\n                return \"\";\n            });\n        }\n    }\n\n    private static class GuiContainerHandler implements IGuiContainerHandler<TemplateManagerGUI> {\n        @Override\n        public List<Rect2i> getGuiExtraAreas(TemplateManagerGUI containerScreen) {\n            return new ArrayList<>(Collections.singleton(new Rect2i((containerScreen.width / 2) + 80, (containerScreen.height / 2) - 80, 60, 120)));\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/client/ClientProxy.java",
    "content": "package com.direwolf20.buildinggadgets.client;\n\nimport com.direwolf20.buildinggadgets.client.cache.CacheTemplateProvider;\nimport com.direwolf20.buildinggadgets.client.events.EventTooltip;\nimport com.direwolf20.buildinggadgets.client.renderer.EffectBlockTER;\nimport com.direwolf20.buildinggadgets.client.screen.TemplateManagerGUI;\nimport com.direwolf20.buildinggadgets.common.blocks.ConstructionBlock;\nimport com.direwolf20.buildinggadgets.common.blocks.OurBlocks;\nimport com.direwolf20.buildinggadgets.common.containers.OurContainers;\nimport com.direwolf20.buildinggadgets.common.items.ConstructionPasteContainer;\nimport com.direwolf20.buildinggadgets.common.items.GadgetCopyPaste;\nimport com.direwolf20.buildinggadgets.common.items.OurItems;\nimport com.direwolf20.buildinggadgets.common.tileentities.ConstructionBlockTileEntity;\nimport com.direwolf20.buildinggadgets.common.tileentities.OurTileEntities;\nimport com.direwolf20.buildinggadgets.common.util.ref.Reference;\nimport net.minecraft.client.Minecraft;\nimport net.minecraft.client.gui.screens.MenuScreens;\nimport net.minecraft.client.renderer.RenderType;\nimport net.minecraft.client.renderer.block.model.BakedQuad;\nimport net.minecraft.client.renderer.block.model.ItemOverrides;\nimport net.minecraft.client.renderer.blockentity.BlockEntityRenderers;\nimport net.minecraft.client.renderer.item.ItemProperties;\nimport net.minecraft.client.renderer.texture.TextureAtlasSprite;\nimport net.minecraft.client.resources.model.BakedModel;\nimport net.minecraft.client.resources.model.ModelResourceLocation;\nimport net.minecraft.client.resources.sounds.SimpleSoundInstance;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.resources.ResourceLocation;\nimport net.minecraft.sounds.SoundEvent;\nimport net.minecraft.util.Mth;\nimport net.minecraft.util.RandomSource;\nimport net.minecraft.world.level.BlockAndTintGetter;\nimport net.minecraft.world.level.block.Blocks;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraftforge.api.distmarker.Dist;\nimport net.minecraftforge.client.ChunkRenderTypeSet;\nimport net.minecraftforge.client.event.ModelEvent;\nimport net.minecraftforge.client.event.RegisterClientTooltipComponentFactoriesEvent;\nimport net.minecraftforge.client.event.RegisterColorHandlersEvent;\nimport net.minecraftforge.client.model.IDynamicBakedModel;\nimport net.minecraftforge.client.model.data.ModelData;\nimport net.minecraftforge.common.MinecraftForge;\nimport net.minecraftforge.event.entity.player.PlayerEvent.PlayerLoggedOutEvent;\nimport net.minecraftforge.eventbus.api.SubscribeEvent;\nimport net.minecraftforge.fml.common.Mod;\nimport org.jetbrains.annotations.NotNull;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport javax.annotation.Nonnull;\nimport javax.annotation.Nullable;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Objects;\n\n@Mod.EventBusSubscriber(value = Dist.CLIENT, bus = Mod.EventBusSubscriber.Bus.MOD)\npublic class ClientProxy {\n    private static final Logger LOGGER = LoggerFactory.getLogger(ClientProxy.class);\n    public static final CacheTemplateProvider CACHE_TEMPLATE_PROVIDER = new CacheTemplateProvider();\n\n    public static void clientSetup() {\n        LOGGER.debug(\"Setting up client for {}\", Reference.MODID);\n        KeyBindings.init();\n\n        BlockEntityRenderers.register(OurTileEntities.EFFECT_BLOCK_TILE_ENTITY.get(), EffectBlockTER::new);\n        MenuScreens.register(OurContainers.TEMPLATE_MANAGER_CONTAINER.get(), TemplateManagerGUI::new);\n\n        MinecraftForge.EVENT_BUS.addListener(ClientProxy::onPlayerLoggedOut);\n        CACHE_TEMPLATE_PROVIDER.registerUpdateListener(((GadgetCopyPaste) OurItems.COPY_PASTE_GADGET_ITEM.get()).getRender());\n\n        List.of(\n                OurItems.PASTE_CONTAINER_T1_ITEM,\n                OurItems.PASTE_CONTAINER_T2_ITEM,\n                OurItems.PASTE_CONTAINER_T3_ITEM,\n                OurItems.PASTE_CONTAINER_CREATIVE_ITEM\n        ).forEach(item -> {\n            ItemProperties.register(item.get(), ConstructionPasteContainer.LEVEL, (stack, clientLevel, entity, notSure) -> {\n                float percent = ConstructionPasteContainer.getPasteAmount(stack) / (float) ConstructionPasteContainer.getMaxPasteAmount(stack);\n                return Mth.floor(percent * 4) / 4F;\n            });\n        });\n    }\n\n    @SubscribeEvent\n    public static void registerConstructionBlockColorHandler(RegisterColorHandlersEvent.Block event) {\n        LOGGER.debug(\"Registering color handlers for {}\", Reference.MODID);\n\n        event.register((state, world, pos, tintIndex) -> {\n            if (world != null) {\n                BlockState mimicBlock = ConstructionBlock.getActualMimicBlock(world, pos);\n                if (mimicBlock == null) {\n                    return -1;\n                }\n\n                try {\n                    return event.getBlockColors().getColor(mimicBlock, world, pos, tintIndex);\n                } catch (Exception var8) {\n                    return -1;\n                }\n            }\n            return -1;\n        }, OurBlocks.CONSTRUCTION_BLOCK.get());\n    }\n\n    @SubscribeEvent\n    public static void registerTooltipFactory(RegisterClientTooltipComponentFactoriesEvent event) {\n        LOGGER.debug(\"Registering custom tooltip component factories for {}\", Reference.MODID);\n        event.register(EventTooltip.CopyPasteTooltipComponent.Data.class, EventTooltip.CopyPasteTooltipComponent::new);\n    }\n\n    public static void playSound(SoundEvent sound, float pitch) {\n        Minecraft.getInstance().getSoundManager().play(SimpleSoundInstance.forUI(sound, pitch));\n    }\n\n    private static void onPlayerLoggedOut(PlayerLoggedOutEvent event) {\n        CACHE_TEMPLATE_PROVIDER.clear();\n    }\n\n    @SubscribeEvent\n    public static void bakeModels(ModelEvent.ModifyBakingResult event) {\n        LOGGER.debug(\"Registering baked models for {}\", Reference.MODID);\n\n        ResourceLocation ConstrName = new ResourceLocation(Reference.MODID, \"construction_block\");\n        ModelResourceLocation ConstrLocation1 = new ModelResourceLocation(ConstrName, \"ambient_occlusion=false,bright=false,neighbor_brightness=false\");\n        ModelResourceLocation ConstrLocation1a = new ModelResourceLocation(ConstrName, \"ambient_occlusion=true,bright=false,neighbor_brightness=false\");\n        ModelResourceLocation ConstrLocation2 = new ModelResourceLocation(ConstrName, \"ambient_occlusion=false,bright=true,neighbor_brightness=false\");\n        ModelResourceLocation ConstrLocation2a = new ModelResourceLocation(ConstrName, \"ambient_occlusion=true,bright=true,neighbor_brightness=false\");\n        ModelResourceLocation ConstrLocation3 = new ModelResourceLocation(ConstrName, \"ambient_occlusion=false,bright=false,neighbor_brightness=true\");\n        ModelResourceLocation ConstrLocation3a = new ModelResourceLocation(ConstrName, \"ambient_occlusion=true,bright=false,neighbor_brightness=true\");\n        ModelResourceLocation ConstrLocation4 = new ModelResourceLocation(ConstrName, \"ambient_occlusion=false,bright=true,neighbor_brightness=true\");\n        ModelResourceLocation ConstrLocation4a = new ModelResourceLocation(ConstrName, \"ambient_occlusion=true,bright=true,neighbor_brightness=true\");\n\n        IDynamicBakedModel bakedModelLoader = new ConstructionBakedModel();\n        IDynamicBakedModel bakedModelLoaderAmbient = new ConstructionBakedModel();\n\n        event.getModels().put(ConstrLocation1, bakedModelLoader);\n        event.getModels().put(ConstrLocation2, bakedModelLoader);\n        event.getModels().put(ConstrLocation3, bakedModelLoader);\n        event.getModels().put(ConstrLocation4, bakedModelLoader);\n        event.getModels().put(ConstrLocation1a, bakedModelLoaderAmbient);\n        event.getModels().put(ConstrLocation2a, bakedModelLoaderAmbient);\n        event.getModels().put(ConstrLocation3a, bakedModelLoaderAmbient);\n        event.getModels().put(ConstrLocation4a, bakedModelLoaderAmbient);\n    }\n\n    static class ConstructionBakedModel implements IDynamicBakedModel {\n        /**\n         * This kinda works most of the time but isn't perfect. Some issues are definitely present.\n         * <p/>\n         * Due to not having access to the block state from the getParticleIcon, we're not able to do this dynamically,\n         * so instead we wait until the getQuads method is called and effectively cache the lookup for the getParticleIcons\n         */\n        TextureAtlasSprite particleAtlas = null;\n\n        @NotNull\n        @Override\n        public List<BakedQuad> getQuads(@Nullable BlockState state, @Nullable Direction side, RandomSource rand, ModelData modelData, RenderType type) {\n            BlockState facadeState = modelData.get(ConstructionBlockTileEntity.FACADE_STATE);\n            if (facadeState == null || facadeState == Blocks.AIR.defaultBlockState())\n                facadeState = OurBlocks.CONSTRUCTION_DENSE_BLOCK.get().defaultBlockState();\n\n            if (particleAtlas == null) {\n                particleAtlas = Minecraft.getInstance().getBlockRenderer().getBlockModel(facadeState).getParticleIcon(modelData);\n            }\n\n            BakedModel model = Minecraft.getInstance().getBlockRenderer().getBlockModelShaper().getBlockModel(facadeState);\n            if (type != null && !model.getRenderTypes(facadeState, rand, modelData).contains(type)) { // always render in the null layer or the block-breaking textures don't show up\n                return Collections.emptyList();\n            }\n\n            return model.getQuads(facadeState, side, rand, modelData, type);\n        }\n\n        @NotNull\n        @Override\n        public TextureAtlasSprite getParticleIcon() {\n            //Fixes a crash until forge does something\n            return Objects.requireNonNullElseGet(particleAtlas,\n                    () -> Minecraft.getInstance().getBlockRenderer().getBlockModel(Blocks.STONE.defaultBlockState()).getParticleIcon(ModelData.EMPTY));\n        }\n\n        @Override\n        public boolean isGui3d() {\n            return false;\n        }\n\n        @Override\n        public boolean usesBlockLight() {\n            return false;\n        } // is side lit maybe?\n\n        @Override\n        public boolean isCustomRenderer() {\n            return false;\n        }\n\n        @Override\n        public boolean useAmbientOcclusion() {\n            return true;\n        }\n\n        @NotNull\n        @Override\n        public ItemOverrides getOverrides() {\n            return null;\n        }\n\n        @Nonnull\n        @Override\n        public ModelData getModelData(@Nonnull BlockAndTintGetter world, @Nonnull BlockPos pos, @Nonnull BlockState state, @Nonnull ModelData tileData) {\n            return tileData;\n        }\n\n        @NotNull\n        @Override\n        public ChunkRenderTypeSet getRenderTypes(@NotNull BlockState state, @NotNull RandomSource rand, @NotNull ModelData data) {\n            return ChunkRenderTypeSet.all();\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/client/KeyBindings.java",
    "content": "package com.direwolf20.buildinggadgets.client;\n\nimport com.direwolf20.buildinggadgets.common.items.AbstractGadget;\nimport com.direwolf20.buildinggadgets.common.items.TemplateItem;\nimport com.direwolf20.buildinggadgets.common.util.ref.Reference;\nimport com.mojang.blaze3d.platform.InputConstants;\nimport net.minecraft.client.KeyMapping;\nimport net.minecraft.client.Minecraft;\nimport net.minecraft.world.InteractionHand;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraftforge.api.distmarker.Dist;\nimport net.minecraftforge.client.event.RegisterKeyMappingsEvent;\nimport net.minecraftforge.client.settings.IKeyConflictContext;\nimport net.minecraftforge.client.settings.KeyConflictContext;\nimport net.minecraftforge.eventbus.api.SubscribeEvent;\nimport net.minecraftforge.fml.common.Mod;\nimport org.lwjgl.glfw.GLFW;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n@Mod.EventBusSubscriber(value = Dist.CLIENT, bus = Mod.EventBusSubscriber.Bus.MOD)\npublic class KeyBindings {\n    private static final Logger LOGGER = LoggerFactory.getLogger(KeyBindings.class);\n\n    private static final KeyConflictContextGadget CONFLICT_CONTEXT_GADGET = new KeyConflictContextGadget();\n\n    private static final List<KeyMapping> keyMappings = new ArrayList<>();\n\n    public static KeyMapping menuSettings = createBinding(\"settings_menu\",GLFW.GLFW_KEY_G);\n    public static KeyMapping range = createBinding(\"range\", GLFW.GLFW_KEY_R);\n    public static KeyMapping undo = createBinding(\"undo\", GLFW.GLFW_KEY_U);\n    public static KeyMapping anchor = createBinding(\"anchor\", GLFW.GLFW_KEY_H);\n    public static KeyMapping fuzzy = createBinding(\"fuzzy\", GLFW.GLFW_KEY_UNKNOWN);\n    public static KeyMapping connectedArea = createBinding(\"connected_area\", GLFW.GLFW_KEY_UNKNOWN);\n    public static KeyMapping rotateMirror = createBinding(\"rotate_mirror\", GLFW.GLFW_KEY_UNKNOWN);\n    public static KeyMapping materialList = createBinding(\"material_list\", GLFW.GLFW_KEY_M);\n\n    public static void init() {}\n\n    private static KeyMapping createBinding(String name, int key) {\n        KeyMapping keyBinding = new KeyMapping(getKey(name), CONFLICT_CONTEXT_GADGET, InputConstants.Type.KEYSYM.getOrCreate(key), getKey(\"category\"));\n        keyMappings.add(keyBinding);\n        return keyBinding;\n    }\n\n    private static String getKey(String name) {\n        return String.join(\".\", \"key\", Reference.MODID, name);\n    }\n\n    @SubscribeEvent\n    static void register(RegisterKeyMappingsEvent event) {\n        LOGGER.debug(\"Registering {} keybinding for {}\", keyMappings.size(), Reference.MODID);\n        keyMappings.forEach(event::register);\n    }\n\n    public static class KeyConflictContextGadget implements IKeyConflictContext\n    {\n        @Override\n        public boolean isActive() {\n            Player player = Minecraft.getInstance().player;\n            return !KeyConflictContext.GUI.isActive() && player != null\n                    && (!AbstractGadget.getGadget(player).isEmpty()\n                        || (player.getItemInHand(InteractionHand.MAIN_HAND).getItem() instanceof TemplateItem || player.getItemInHand(InteractionHand.OFF_HAND).getItem() instanceof TemplateItem));\n        }\n\n        @Override\n        public boolean conflicts(IKeyConflictContext other) {\n            return other == this || other == KeyConflictContext.IN_GAME;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/client/OurSounds.java",
    "content": "package com.direwolf20.buildinggadgets.client;\n\nimport com.direwolf20.buildinggadgets.common.util.ref.Reference;\nimport net.minecraft.resources.ResourceLocation;\nimport net.minecraft.sounds.SoundEvent;\nimport net.minecraftforge.registries.DeferredRegister;\nimport net.minecraftforge.registries.ForgeRegistries;\nimport net.minecraftforge.registries.RegistryObject;\n\npublic interface OurSounds {\n    DeferredRegister<SoundEvent> REGISTRY = DeferredRegister.create(ForgeRegistries.SOUND_EVENTS, Reference.MODID);\n\n    RegistryObject<SoundEvent> BEEP = REGISTRY.register(\"beep\", () -> SoundEvent.createVariableRangeEvent(new ResourceLocation(Reference.MODID, \"beep\")));\n\n    static void playSound(SoundEvent sound, float pitch) {\n        ClientProxy.playSound(sound, pitch);\n    }\n\n    static void playSound(SoundEvent sound) {\n        ClientProxy.playSound(sound, 1.0F);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/client/cache/CacheTemplateProvider.java",
    "content": "package com.direwolf20.buildinggadgets.client.cache;\n\nimport com.direwolf20.buildinggadgets.common.BuildingGadgets;\nimport com.direwolf20.buildinggadgets.common.network.PacketHandler;\nimport com.direwolf20.buildinggadgets.common.network.packets.PacketRequestTemplate;\nimport com.direwolf20.buildinggadgets.common.network.packets.SplitPacketUpdateTemplate;\nimport com.direwolf20.buildinggadgets.common.tainted.Tainted;\nimport com.direwolf20.buildinggadgets.common.tainted.template.ITemplateKey;\nimport com.direwolf20.buildinggadgets.common.tainted.template.ITemplateProvider;\nimport com.direwolf20.buildinggadgets.common.tainted.template.Template;\nimport com.google.common.cache.Cache;\nimport com.google.common.cache.CacheBuilder;\nimport net.minecraftforge.network.PacketDistributor;\nimport org.apache.logging.log4j.util.TriConsumer;\n\nimport javax.annotation.Nonnull;\nimport java.util.Collections;\nimport java.util.Set;\nimport java.util.UUID;\nimport java.util.WeakHashMap;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.TimeUnit;\nimport java.util.function.Function;\n\n@Tainted(reason = \"Uses template system\")\npublic final class CacheTemplateProvider implements ITemplateProvider {\n    private final Cache<UUID, Template> cache;\n    private final Set<IUpdateListener> updateListeners;\n\n    public CacheTemplateProvider() {\n        this.cache = CacheBuilder\n                .newBuilder()\n                .expireAfterAccess(1, TimeUnit.MINUTES)\n                .build();\n        this.updateListeners = Collections.newSetFromMap(new WeakHashMap<>());\n    }\n\n    @Override\n    @Nonnull\n    public Template getTemplateForKey(@Nonnull ITemplateKey key) {\n        UUID id = getId(key);\n        try {\n            return cache.get(id, () -> {\n                requestUpdate(id, PacketDistributor.SERVER.noArg());\n                return new Template();\n            });\n        } catch (ExecutionException e) {\n            BuildingGadgets.LOG.error(\"Failed to access Cache! Returning new Template, this is certainly going to cause unexpected behaviour!\", e);\n            return new Template();\n        }\n    }\n\n    @Override\n    public void setTemplate(ITemplateKey key, Template template) {\n        UUID id = getId(key);\n        cache.put(id, template);\n        notifyListeners(key, template, l -> l::onTemplateUpdate);\n    }\n\n    @Override\n    public boolean requestUpdate(ITemplateKey key) {\n        return requestUpdate(key, PacketDistributor.SERVER.noArg());\n    }\n\n    @Override\n    public boolean requestUpdate(ITemplateKey key, PacketDistributor.PacketTarget target) {\n        return requestUpdate(key.getTemplateId(UUID::randomUUID), target);\n    }\n\n    private boolean requestUpdate(UUID id, PacketDistributor.PacketTarget target) {\n        PacketHandler.send(new PacketRequestTemplate(id), target);\n        return true;\n    }\n\n    @Override\n    public boolean requestRemoteUpdate(ITemplateKey key, PacketDistributor.PacketTarget target) {\n        UUID id = getId(key);\n        Template template = cache.getIfPresent(id);\n        if (template != null) {\n            notifyListeners(key, template, l -> l::onTemplateUpdateSend);\n            PacketHandler.getSplitManager().send(new SplitPacketUpdateTemplate(id, template), target);\n        }\n        return template != null;\n    }\n\n    @Override\n    public boolean requestRemoteUpdate(ITemplateKey key) {\n        return requestRemoteUpdate(key, PacketDistributor.SERVER.noArg());\n    }\n\n    @Override\n    public void registerUpdateListener(IUpdateListener listener) {\n        updateListeners.add(listener);\n    }\n\n    @Override\n    public void removeUpdateListener(IUpdateListener listener) {\n        updateListeners.remove(listener);\n    }\n\n    @Override\n    public UUID getId(ITemplateKey key) {\n        return key.getTemplateId(UUID::randomUUID);\n    }\n\n    /**\n     * Although public, do not use this method willingly. The cache is already purged on each\n     * onPlayerLoggedOut event.\n     */\n    public void clear() {\n        this.cache.invalidateAll();\n        this.cache.cleanUp();\n    }\n\n    private void notifyListeners(ITemplateKey key, Template template, Function<IUpdateListener, TriConsumer<ITemplateProvider, ITemplateKey, Template>> function) {\n        for (IUpdateListener listener : updateListeners) {\n            try {\n                function.apply(listener).accept(this, key, template);\n            } catch (Exception e) {\n                BuildingGadgets.LOG.error(\"Update listener threw an exception!\", e);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/client/cache/RemoteInventoryCache.java",
    "content": "package com.direwolf20.buildinggadgets.client.cache;\n\nimport com.direwolf20.buildinggadgets.common.network.PacketHandler;\nimport com.direwolf20.buildinggadgets.common.network.packets.PacketSetRemoteInventoryCache;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.InventoryLinker;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.materials.objects.UniqueItem;\nimport com.google.common.base.Stopwatch;\nimport com.google.common.collect.Multiset;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.resources.ResourceKey;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.world.level.Level;\nimport org.apache.commons.lang3.tuple.Pair;\n\nimport javax.annotation.Nullable;\nimport java.util.Objects;\nimport java.util.concurrent.TimeUnit;\n\npublic class RemoteInventoryCache {\n    private boolean isCopyPaste, forceUpdate;\n    private Pair<BlockPos, ResourceKey<Level>> locCached;\n    private Multiset<UniqueItem> cache;\n    private Stopwatch timer;\n\n    public RemoteInventoryCache(boolean isCopyPaste) {\n        this.isCopyPaste = isCopyPaste;\n    }\n\n    public void setCache(Multiset<UniqueItem> cache) {\n        this.cache = cache;\n    }\n\n    public void forceUpdate() {\n        forceUpdate = true;\n    }\n\n    public boolean maintainCache(ItemStack tool) {\n        Pair<BlockPos, ResourceKey<Level>> loc = InventoryLinker.getDataFromStack(tool);\n        if (isCacheOld(loc))\n            updateCache(loc);\n\n        return loc != null;\n    }\n\n    public Multiset<UniqueItem> getCache() {\n        return cache;\n    }\n\n    private void updateCache(Pair<BlockPos, ResourceKey<Level>> loc) {\n        locCached = loc;\n        if (loc == null)\n            cache = null;\n        else {\n            PacketHandler.sendToServer(new PacketSetRemoteInventoryCache(loc, isCopyPaste));\n        }\n    }\n\n    private boolean isCacheOld(@Nullable Pair<BlockPos, ResourceKey<Level>> loc) {\n        if (!Objects.equals(locCached, loc)) {\n            timer = loc == null ? null : Stopwatch.createStarted();\n            return true;\n        }\n        if (timer != null) {\n            boolean overtime = forceUpdate || timer.elapsed(TimeUnit.MILLISECONDS) >= 5000;\n            if (overtime) {\n                timer.reset();\n                timer.start();\n                forceUpdate = false;\n            }\n            return overtime;\n        }\n        return false;\n    }\n}"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/client/events/EventKeyInput.java",
    "content": "package com.direwolf20.buildinggadgets.client.events;\n\nimport com.direwolf20.buildinggadgets.client.KeyBindings;\nimport com.direwolf20.buildinggadgets.client.screen.GuiMod;\nimport com.direwolf20.buildinggadgets.client.screen.ModeRadialMenu;\nimport com.direwolf20.buildinggadgets.common.items.AbstractGadget;\nimport com.direwolf20.buildinggadgets.common.network.PacketHandler;\nimport com.direwolf20.buildinggadgets.common.network.packets.*;\nimport com.direwolf20.buildinggadgets.common.util.ref.Reference;\nimport net.minecraft.client.Minecraft;\nimport net.minecraft.client.KeyMapping;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraftforge.api.distmarker.Dist;\nimport net.minecraftforge.client.settings.KeyModifier;\nimport net.minecraftforge.event.TickEvent.ClientTickEvent;\nimport net.minecraftforge.event.TickEvent.Phase;\nimport net.minecraftforge.eventbus.api.SubscribeEvent;\nimport net.minecraftforge.fml.common.Mod.EventBusSubscriber;\n\n@EventBusSubscriber(modid = Reference.MODID, value = Dist.CLIENT)\npublic class EventKeyInput {\n\n    @SubscribeEvent\n    public static void handleEventInput(ClientTickEvent event) {\n        Minecraft mc = Minecraft.getInstance();\n        if (mc.player == null || event.phase == Phase.START)\n            return;\n\n        if (KeyBindings.materialList.consumeClick()) {\n            GuiMod.MATERIAL_LIST.openScreen(mc.player);\n            return;\n        }\n\n        ItemStack tool = AbstractGadget.getGadget(mc.player);\n        if (tool.isEmpty())\n            return;\n\n        KeyMapping mode = KeyBindings.menuSettings;\n        if (!(mc.screen instanceof ModeRadialMenu) && mode.consumeClick() && ((mode.getKeyModifier() == KeyModifier.NONE\n                && KeyModifier.getActiveModifier() == KeyModifier.NONE) || mode.getKeyModifier() != KeyModifier.NONE)) {\n                mc.setScreen(new ModeRadialMenu(tool));\n        } else if (KeyBindings.range.consumeClick()) {\n            PacketHandler.sendToServer(new PacketChangeRange());\n        } else if (KeyBindings.rotateMirror.consumeClick()) {\n            PacketHandler.sendToServer(new PacketRotateMirror());\n        } else if (KeyBindings.undo.consumeClick()) {\n            PacketHandler.sendToServer(new PacketUndo());\n        } else if (KeyBindings.anchor.consumeClick()) {\n            PacketHandler.sendToServer(new PacketAnchor());\n        } else if (KeyBindings.fuzzy.consumeClick()) {\n            PacketHandler.sendToServer(new PacketToggleFuzzy());\n        } else if (KeyBindings.connectedArea.consumeClick()) {\n            PacketHandler.sendToServer(new PacketToggleConnectedArea());\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/client/events/EventRenderWorldLast.java",
    "content": "package com.direwolf20.buildinggadgets.client.events;\n\nimport com.direwolf20.buildinggadgets.common.items.AbstractGadget;\nimport com.direwolf20.buildinggadgets.common.util.ref.Reference;\nimport net.minecraft.client.Minecraft;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraftforge.api.distmarker.Dist;\nimport net.minecraftforge.client.event.RenderLevelStageEvent;\nimport net.minecraftforge.eventbus.api.SubscribeEvent;\nimport net.minecraftforge.fml.common.Mod;\n\n@Mod.EventBusSubscriber(modid = Reference.MODID, value = Dist.CLIENT)\npublic class EventRenderWorldLast {\n\n    @SubscribeEvent\n    static void RenderLevelLastEvent(RenderLevelStageEvent evt) {\n        if (evt.getStage() != RenderLevelStageEvent.Stage.AFTER_TRANSLUCENT_BLOCKS) {\n            return;\n        }\n\n        Player player = Minecraft.getInstance().player;\n        if( player == null )\n            return;\n\n        ItemStack heldItem = AbstractGadget.getGadget(player);\n        if (heldItem.isEmpty())\n            return;\n\n        ((AbstractGadget) heldItem.getItem()).getRender().render(evt, player, heldItem);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/client/events/EventTooltip.java",
    "content": "package com.direwolf20.buildinggadgets.client.events;\n\nimport com.direwolf20.buildinggadgets.client.cache.RemoteInventoryCache;\nimport com.direwolf20.buildinggadgets.common.capability.CapabilityTemplate;\nimport com.direwolf20.buildinggadgets.common.items.GadgetCopyPaste;\nimport com.direwolf20.buildinggadgets.common.items.OurItems;\nimport com.direwolf20.buildinggadgets.common.tainted.building.view.BuildContext;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.IItemIndex;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.InventoryHelper;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.MatchResult;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.materials.MaterialList;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.materials.objects.IUniqueObject;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.materials.objects.UniqueItem;\nimport com.direwolf20.buildinggadgets.common.tainted.template.Template;\nimport com.direwolf20.buildinggadgets.common.tainted.template.TemplateHeader;\nimport com.google.common.collect.ImmutableList;\nimport com.google.common.collect.ImmutableMultiset;\nimport com.google.common.collect.Multiset;\nimport com.google.common.collect.Multiset.Entry;\nimport com.mojang.blaze3d.systems.RenderSystem;\nimport com.mojang.blaze3d.vertex.PoseStack;\nimport net.minecraft.client.Minecraft;\nimport net.minecraft.client.gui.Font;\nimport net.minecraft.client.gui.screens.Screen;\nimport net.minecraft.client.gui.screens.inventory.tooltip.ClientTooltipComponent;\nimport net.minecraft.client.renderer.MultiBufferSource;\nimport net.minecraft.client.renderer.entity.ItemRenderer;\nimport net.minecraft.world.inventory.tooltip.TooltipComponent;\nimport net.minecraft.world.item.ItemStack;\nimport org.lwjgl.opengl.GL11;\n\nimport java.util.Comparator;\nimport java.util.List;\n\npublic class EventTooltip {\n    private static final Comparator<Multiset.Entry<IUniqueObject<?>>> ENTRY_COMPARATOR = Comparator\n            .<Multiset.Entry<IUniqueObject<?>>, Integer>comparing(Entry::getCount)\n            .reversed()\n            .thenComparing(e -> e.getElement().getObjectRegistryName());\n\n    private static final int STACKS_PER_LINE = 16;\n    private static RemoteInventoryCache cache = new RemoteInventoryCache(true);\n\n    public static void setCache(Multiset<UniqueItem> cache) {\n        EventTooltip.cache.setCache(cache);\n    }\n\n    public static class CopyPasteTooltipComponent implements ClientTooltipComponent {\n        Data tooltipData;\n\n        public CopyPasteTooltipComponent(Data tooltipComponent) {\n            tooltipData = tooltipComponent;\n        }\n\n        @Override\n        public int getHeight() {\n            return Screen.hasShiftDown() && tooltipData.data != null ? 20 + (tooltipData.data.sortedEntries.size() / STACKS_PER_LINE * 20) : 0;\n        }\n\n        @Override\n        public int getWidth(Font font) {\n            return Screen.hasShiftDown() && tooltipData.data != null ? (tooltipData.data.sortedEntries.size() <= STACKS_PER_LINE ? tooltipData.data.sortedEntries.size() * 18 : STACKS_PER_LINE * 18) : 0;\n        }\n\n        @Override\n        public void renderImage(Font font, int x, int y, PoseStack poseStack, ItemRenderer itemRenderer) {\n            if (this.tooltipData.stack == null || !(this.tooltipData.stack.getItem() instanceof GadgetCopyPaste))\n                return;\n\n            Minecraft mc = Minecraft.getInstance();\n            if (mc.level == null || mc.player == null || !Screen.hasShiftDown() || tooltipData.data == null)\n                return;\n\n\n            int bx = x;\n            int by = y;\n            int j = 0;\n            int totalMissing = 0;\n\n            //add missing offset because the Stack is 16 by 16 as a render, not 9 by 9\n            //needs to be 8 instead of 7, so that there is a one pixel padding to the text, just as there is between stacks\n//            by += 8;\n            RenderSystem.enableBlend();\n            RenderSystem.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);\n            for (Multiset.Entry<IUniqueObject<?>> entry : tooltipData.data.sortedEntries) {\n                int xx = bx + (j % STACKS_PER_LINE) * 18;\n                int yy = by + (j / STACKS_PER_LINE) * 20;\n                totalMissing += renderRequiredBlocks(poseStack, itemRenderer, entry.getElement().createStack(), xx, yy, tooltipData.data.existing.count(entry.getElement()), entry.getCount());\n                j++;\n            }\n\n            if (!tooltipData.data.match.isSuccess()) {\n                IUniqueObject<?> pasteItem = new UniqueItem(OurItems.CONSTRUCTION_PASTE_ITEM.get());\n                Multiset<IUniqueObject<?>> pasteSet = ImmutableMultiset.<IUniqueObject<?>>builder()\n                        .addCopies(pasteItem, totalMissing)\n                        .build();\n\n                int hasAmt = tooltipData.data.index.tryMatch(pasteSet).getFoundItems().count(pasteItem);\n                int xx = bx + (j % STACKS_PER_LINE) * 18;\n                int yy = by + (j / STACKS_PER_LINE) * 20;\n\n                int required = Integer.MAX_VALUE;\n                try {\n                    required = Math.toIntExact(totalMissing);\n                } catch (ArithmeticException ignored) {\n                }\n\n                renderRequiredBlocks(poseStack, itemRenderer, pasteItem.createStack(), xx, yy, hasAmt, required);\n            }\n        }\n\n        public static class Data implements TooltipComponent {\n            public ItemStack stack;\n            public TemplateData data;\n\n            public Data(ItemStack stack) {\n                this.stack = stack;\n\n                Minecraft mc = Minecraft.getInstance();\n                mc.level.getCapability(CapabilityTemplate.TEMPLATE_PROVIDER_CAPABILITY).ifPresent(provider -> stack.getCapability(CapabilityTemplate.TEMPLATE_KEY_CAPABILITY).ifPresent(templateKey -> {\n                    Template template = provider.getTemplateForKey(templateKey);\n\n                    IItemIndex index = InventoryHelper.index(stack, mc.player);\n                    BuildContext buildContext = BuildContext.builder()\n                            .stack(stack)\n                            .player(mc.player)\n                            .build(mc.level);\n\n                    TemplateHeader header = template.getHeaderAndForceMaterials(buildContext);\n                    MaterialList list = header.getRequiredItems();\n                    if (list == null)\n                        list = MaterialList.empty();\n\n                    MatchResult match = index.tryMatch(list);\n                    Multiset<IUniqueObject<?>> existing = match.getFoundItems();\n                    List<Entry<IUniqueObject<?>>> sortedEntries = ImmutableList.sortedCopyOf(ENTRY_COMPARATOR, match.getChosenOption().entrySet());\n\n                    this.data = new TemplateData(existing, sortedEntries, index, match);\n                }));\n            }\n        }\n\n        public record TemplateData(Multiset<IUniqueObject<?>> existing,\n                                   List<Entry<IUniqueObject<?>>> sortedEntries,\n                                   IItemIndex index,\n                                   MatchResult match) {\n        }\n    }\n\n    private static int renderRequiredBlocks(PoseStack matrices, ItemRenderer itemRenderer, ItemStack itemStack, int x, int y, int count, int req) {\n        Minecraft mc = Minecraft.getInstance();\n\n        String s1 = req == Integer.MAX_VALUE ? \"\\u221E\" : Integer.toString(req);\n        int w1 = mc.font.width(s1);\n\n        boolean hasReq = req > 0;\n\n        itemRenderer.renderAndDecorateItem(matrices, itemStack, x, y);\n        itemRenderer.renderGuiItemDecorations(matrices, mc.font, itemStack, x, y);\n\n        MultiBufferSource.BufferSource irendertypebuffer$impl = Minecraft.getInstance().renderBuffers().bufferSource();\n\n        matrices.pushPose();\n        matrices.translate(x + 8 - w1 / 4f, y + (hasReq ? 12 : 14), ItemRenderer.ITEM_COUNT_BLIT_OFFSET + 250);\n        matrices.scale(.5f, .5f, 0);\n        mc.font.draw(matrices, s1, 0, 0, 0xFFFFFF);\n        matrices.popPose();\n\n        int missingCount = 0;\n        if (hasReq) {\n\n            if (count < req) {\n                String fs = Integer.toString(req - count);\n                String s2 = \"(\" + fs + \")\";\n                int w2 = mc.font.width(s2);\n\n                matrices.pushPose();\n                matrices.translate(x + 8 - w2 / 4f, y + 17, ItemRenderer.ITEM_COUNT_BLIT_OFFSET + 250);\n                matrices.scale(.5f, .5f, 0);\n                mc.font.drawInBatch(s2, 0, 0, 0xFF0000, true, matrices.last().pose(), irendertypebuffer$impl, Font.DisplayMode.NORMAL, 0, 15728880);\n                matrices.popPose();\n\n                missingCount = (req - count);\n            }\n        }\n\n        irendertypebuffer$impl.endBatch();\n\n        return missingCount;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/client/models/ConstructionBakedModel.java",
    "content": "package com.direwolf20.buildinggadgets.client.models;\n\nimport com.direwolf20.buildinggadgets.common.blocks.OurBlocks;\nimport com.direwolf20.buildinggadgets.common.tileentities.ConstructionBlockTileEntity;\nimport net.minecraft.client.Minecraft;\nimport net.minecraft.client.renderer.ItemBlockRenderTypes;\nimport net.minecraft.client.renderer.RenderType;\nimport net.minecraft.client.renderer.block.model.BakedQuad;\nimport net.minecraft.client.renderer.block.model.ItemOverrides;\nimport net.minecraft.client.renderer.texture.TextureAtlasSprite;\nimport net.minecraft.client.resources.model.BakedModel;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.util.RandomSource;\nimport net.minecraft.world.level.BlockAndTintGetter;\nimport net.minecraft.world.level.block.Blocks;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraftforge.client.ForgeHooksClient;\nimport net.minecraftforge.client.model.IDynamicBakedModel;\nimport net.minecraftforge.client.model.data.ModelData;\n\nimport javax.annotation.Nonnull;\nimport javax.annotation.Nullable;\nimport java.util.Collections;\nimport java.util.List;\n\npublic class ConstructionBakedModel implements IDynamicBakedModel {\n    private BlockState facadeState;\n\n    @Override\n    public boolean isGui3d() {\n        return false;\n    }\n\n    @Override\n    public boolean usesBlockLight() {\n        return false;\n    }\n\n    @Override\n    public boolean isCustomRenderer() {\n        return false;\n    }\n\n    @Override\n    public TextureAtlasSprite getParticleIcon() {\n        return null;\n    }\n\n    @Override\n    public boolean useAmbientOcclusion() {\n        if (facadeState == null) return false;\n        BakedModel model;\n        model = Minecraft.getInstance().getBlockRenderer().getBlockModelShaper().getBlockModel(facadeState);\n        return model.useAmbientOcclusion();\n    }\n\n\n    @Override\n    public List<BakedQuad> getQuads(@Nullable BlockState state, @Nullable Direction side, RandomSource rand, ModelData modelData, RenderType type) {\n        BakedModel model;\n        facadeState = modelData.get(ConstructionBlockTileEntity.FACADE_STATE);\n        if (facadeState == null || facadeState == Blocks.AIR.defaultBlockState())\n            facadeState = OurBlocks.CONSTRUCTION_DENSE_BLOCK.get().defaultBlockState();\n        model = Minecraft.getInstance().getBlockRenderer().getBlockModelShaper().getBlockModel(facadeState);\n        if (type != null && !model.getRenderTypes(facadeState, rand, modelData).contains(type)) { // always render in the null layer or the block-breaking textures don't show up\n            return Collections.emptyList();\n        }\n        return model.getQuads(facadeState, side, rand, modelData, type);\n\n    }\n\n//    @Override\n//    public TextureAtlasSprite getParticleTexture(@Nonnull IModelData data) {\n//        return this.getBakedModel().getParticleTexture(data);\n//    }\n\n    @Override\n    public ItemOverrides getOverrides() {\n        return null;\n    }\n\n    @Nonnull\n    @Override\n    public ModelData getModelData(@Nonnull BlockAndTintGetter world, @Nonnull BlockPos pos, @Nonnull BlockState state, @Nonnull ModelData tileData) {\n        return tileData;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/client/renderer/DireBufferBuilder.java",
    "content": "package com.direwolf20.buildinggadgets.client.renderer;\n\nimport com.google.common.collect.ImmutableList;\nimport com.google.common.collect.Lists;\nimport com.google.common.primitives.Floats;\nimport com.mojang.blaze3d.vertex.DefaultedVertexConsumer;\nimport com.mojang.blaze3d.vertex.VertexConsumer;\nimport com.mojang.blaze3d.vertex.BufferVertexConsumer;\nimport com.mojang.datafixers.util.Pair;\nimport it.unimi.dsi.fastutil.ints.IntArrays;\nimport com.mojang.blaze3d.platform.MemoryTracker;\nimport com.mojang.blaze3d.vertex.DefaultVertexFormat;\nimport com.mojang.blaze3d.vertex.VertexFormat;\nimport com.mojang.blaze3d.vertex.VertexFormatElement;\nimport net.minecraftforge.api.distmarker.Dist;\nimport net.minecraftforge.api.distmarker.OnlyIn;\nimport org.apache.logging.log4j.LogManager;\nimport org.apache.logging.log4j.Logger;\n\nimport javax.annotation.Nullable;\nimport java.nio.ByteBuffer;\nimport java.nio.FloatBuffer;\nimport java.util.BitSet;\nimport java.util.List;\n\npublic class DireBufferBuilder extends DefaultedVertexConsumer implements BufferVertexConsumer {\n    private static final Logger LOGGER = LogManager.getLogger();\n    private ByteBuffer byteBuffer;\n    private final List<DireBufferBuilder.DrawState> drawStates = Lists.newArrayList();\n    private int drawStateIndex = 0;\n    private int renderedBytes = 0;\n    private int nextElementBytes = 0;\n    private int uploadedBytes = 0;\n    private int vertexCount;\n    @Nullable\n    private VertexFormatElement vertexFormatElement;\n    private int vertexFormatIndex;\n    private int drawMode;\n    private VertexFormat vertexFormat;\n    private boolean fastFormat;\n    private boolean fullFormat;\n    private boolean isDrawing;\n\n    public DireBufferBuilder(int bufferSizeIn) {\n        this.byteBuffer = MemoryTracker.create(bufferSizeIn * 4); // TODO: might need to remove or change to 6\n    }\n\n    protected void growBuffer() {\n        this.growBuffer(this.vertexFormat.getVertexSize());\n    }\n\n    private void growBuffer(int increaseAmount) {\n        if (this.nextElementBytes + increaseAmount > this.byteBuffer.capacity()) {\n            int i = this.byteBuffer.capacity();\n            int j = i + roundUpPositive(increaseAmount);\n            LOGGER.debug(\"Needed to grow BufferBuilder buffer: Old size {} bytes, new size {} bytes.\", i, j);\n            ByteBuffer bytebuffer = MemoryTracker.create(j);\n            this.byteBuffer.position(0);\n            bytebuffer.put(this.byteBuffer);\n            bytebuffer.rewind();\n            this.byteBuffer = bytebuffer;\n        }\n    }\n\n    private static int roundUpPositive(int xIn) {\n        int i = 2097152;\n        if (xIn == 0) {\n            return i;\n        } else {\n            if (xIn < 0) {\n                i *= -1;\n            }\n\n            int j = xIn % i;\n            return j == 0 ? xIn : xIn + i - j;\n        }\n    }\n\n    public void sortVertexData(float cameraX, float cameraY, float cameraZ) {\n        this.byteBuffer.clear();\n        FloatBuffer floatbuffer = this.byteBuffer.asFloatBuffer();\n        int i = this.vertexCount / 4;\n        float[] afloat = new float[i];\n\n        for (int j = 0; j < i; ++j) {\n            afloat[j] = getDistanceSq(floatbuffer, cameraX, cameraY, cameraZ, this.vertexFormat.getIntegerSize(), this.renderedBytes / 4 + j * this.vertexFormat.getVertexSize());\n        }\n\n        int[] aint = new int[i];\n\n        for (int k = 0; k < aint.length; aint[k] = k++) {\n            ;\n        }\n\n        IntArrays.mergeSort(aint, (p_227830_1_, p_227830_2_) -> {\n            return Floats.compare(afloat[p_227830_1_], afloat[p_227830_2_]);\n        });\n        BitSet bitset = new BitSet();\n        FloatBuffer floatbuffer1 = FloatBuffer.allocate(this.vertexFormat.getIntegerSize() * 6);\n\n        for (int l = bitset.nextClearBit(0); l < aint.length; l = bitset.nextClearBit(l + 1)) {\n            int i1 = aint[l];\n            if (i1 != l) {\n                this.limitToVertex(floatbuffer, i1);\n                floatbuffer1.clear();\n                floatbuffer1.put(floatbuffer);\n                int j1 = i1;\n\n                for (int k1 = aint[i1]; j1 != l; k1 = aint[k1]) {\n                    this.limitToVertex(floatbuffer, k1);\n                    FloatBuffer floatbuffer2 = floatbuffer.slice();\n                    this.limitToVertex(floatbuffer, j1);\n                    floatbuffer.put(floatbuffer2);\n                    bitset.set(j1);\n                    j1 = k1;\n                }\n\n                this.limitToVertex(floatbuffer, l);\n                floatbuffer1.flip();\n                floatbuffer.put(floatbuffer1);\n            }\n\n            bitset.set(l);\n        }\n    }\n\n    private void limitToVertex(FloatBuffer floatBufferIn, int indexIn) {\n        int i = this.vertexFormat.getIntegerSize() * 4;\n        floatBufferIn.limit(this.renderedBytes / 4 + (indexIn + 1) * i);\n        floatBufferIn.position(this.renderedBytes / 4 + indexIn * i);\n    }\n\n    public DireBufferBuilder.State getVertexState() {\n        this.byteBuffer.limit(this.nextElementBytes);\n        this.byteBuffer.position(this.renderedBytes);\n        ByteBuffer bytebuffer = ByteBuffer.allocate(this.vertexCount * this.vertexFormat.getVertexSize());\n        bytebuffer.put(this.byteBuffer);\n        this.byteBuffer.clear();\n        return new DireBufferBuilder.State(bytebuffer, this.vertexFormat);\n    }\n\n    private static float getDistanceSq(FloatBuffer floatBufferIn, float x, float y, float z, int integerSize, int offset) {\n        float f = floatBufferIn.get(offset + integerSize * 0 + 0);\n        float f1 = floatBufferIn.get(offset + integerSize * 0 + 1);\n        float f2 = floatBufferIn.get(offset + integerSize * 0 + 2);\n        float f3 = floatBufferIn.get(offset + integerSize * 1 + 0);\n        float f4 = floatBufferIn.get(offset + integerSize * 1 + 1);\n        float f5 = floatBufferIn.get(offset + integerSize * 1 + 2);\n        float f6 = floatBufferIn.get(offset + integerSize * 2 + 0);\n        float f7 = floatBufferIn.get(offset + integerSize * 2 + 1);\n        float f8 = floatBufferIn.get(offset + integerSize * 2 + 2);\n        float f9 = floatBufferIn.get(offset + integerSize * 3 + 0);\n        float f10 = floatBufferIn.get(offset + integerSize * 3 + 1);\n        float f11 = floatBufferIn.get(offset + integerSize * 3 + 2);\n        float f12 = (f + f3 + f6 + f9) * 0.25F - x;\n        float f13 = (f1 + f4 + f7 + f10) * 0.25F - y;\n        float f14 = (f2 + f5 + f8 + f11) * 0.25F - z;\n        return f12 * f12 + f13 * f13 + f14 * f14;\n    }\n\n    public void setVertexState(DireBufferBuilder.State state) {\n        state.stateByteBuffer.clear();\n        int i = state.stateByteBuffer.capacity();\n        this.growBuffer(i);\n        this.byteBuffer.limit(this.byteBuffer.capacity());\n        this.byteBuffer.position(this.renderedBytes);\n        this.byteBuffer.put(state.stateByteBuffer);\n        this.byteBuffer.clear();\n        VertexFormat vertexformat = state.stateVertexFormat;\n        this.setVertexFormat(vertexformat);\n        this.vertexCount = i / vertexformat.getVertexSize();\n        this.nextElementBytes = this.renderedBytes + this.vertexCount * vertexformat.getVertexSize();\n    }\n\n    public void begin(int glMode, VertexFormat format) {\n        if (this.isDrawing) {\n            throw new IllegalStateException(\"Already building!\");\n        } else {\n            this.isDrawing = true;\n            this.drawMode = glMode;\n            this.setVertexFormat(format);\n            this.vertexFormatElement = format.getElements().get(0);\n            this.vertexFormatIndex = 0;\n            this.byteBuffer.clear();\n        }\n    }\n\n    private void setVertexFormat(VertexFormat vertexFormatIn) {\n        if (this.vertexFormat != vertexFormatIn) {\n            this.vertexFormat = vertexFormatIn;\n            boolean flag = vertexFormatIn == DefaultVertexFormat.POSITION_COLOR_TEX_LIGHTMAP; //POSITION_COLOR_TEXTURE_OVERLAY_LIGHT_NORMAL;\n            boolean flag1 = vertexFormatIn == DefaultVertexFormat.BLOCK;\n            this.fastFormat = flag || flag1;\n            this.fullFormat = flag;\n        }\n    }\n\n    public void finishDrawing() {\n        if (!this.isDrawing) {\n            throw new IllegalStateException(\"Not building!\");\n        } else {\n            this.isDrawing = false;\n            this.drawStates.add(new DireBufferBuilder.DrawState(this.vertexFormat, this.vertexCount, this.drawMode));\n            this.renderedBytes += this.vertexCount * this.vertexFormat.getVertexSize();\n            this.vertexCount = 0;\n            this.vertexFormatElement = null;\n            this.vertexFormatIndex = 0;\n        }\n    }\n\n    public void putByte(int indexIn, byte byteIn) {\n        this.byteBuffer.put(this.nextElementBytes + indexIn, byteIn);\n    }\n\n    public void putShort(int indexIn, short shortIn) {\n        this.byteBuffer.putShort(this.nextElementBytes + indexIn, shortIn);\n    }\n\n    public void putFloat(int indexIn, float floatIn) {\n        this.byteBuffer.putFloat(this.nextElementBytes + indexIn, floatIn);\n    }\n\n    public void endVertex() {\n        if (this.vertexFormatIndex != 0) {\n            throw new IllegalStateException(\"Not filled all elements of the vertex\");\n        } else {\n            ++this.vertexCount;\n            this.growBuffer();\n        }\n    }\n\n    public void nextElement() {\n        ImmutableList<VertexFormatElement> immutablelist = this.vertexFormat.getElements();\n        this.vertexFormatIndex = (this.vertexFormatIndex + 1) % immutablelist.size();\n        this.nextElementBytes += this.vertexFormatElement.getByteSize();\n        VertexFormatElement vertexformatelement = immutablelist.get(this.vertexFormatIndex);\n        this.vertexFormatElement = vertexformatelement;\n        if (vertexformatelement.getUsage() == VertexFormatElement.Usage.PADDING) {\n            this.nextElement();\n        }\n\n        if (this.defaultColorSet && this.vertexFormatElement.getUsage() == VertexFormatElement.Usage.COLOR) {\n            BufferVertexConsumer.super.color(this.defaultR, this.defaultG, this.defaultB, this.defaultA);\n        }\n\n    }\n\n    public VertexConsumer color(int red, int green, int blue, int alpha) {\n        if (this.defaultColorSet) {\n            throw new IllegalStateException();\n        } else {\n            return BufferVertexConsumer.super.color(red, green, blue, alpha);\n        }\n    }\n\n    public void vertex(float x, float y, float z, float red, float green, float blue, float alpha, float texU, float texV, int overlayUV, int lightmapUV, float normalX, float normalY, float normalZ) {\n        if (this.defaultColorSet) {\n            throw new IllegalStateException();\n        } else if (this.fastFormat) {\n            this.putFloat(0, x);\n            this.putFloat(4, y);\n            this.putFloat(8, z);\n            this.putByte(12, (byte) ((int) (red * 255.0F)));\n            this.putByte(13, (byte) ((int) (green * 255.0F)));\n            this.putByte(14, (byte) ((int) (blue * 255.0F)));\n            this.putByte(15, (byte) ((int) (alpha * 255.0F)));\n            this.putFloat(16, texU);\n            this.putFloat(20, texV);\n            int i;\n            if (this.fullFormat) {\n                this.putShort(24, (short) (overlayUV & '\\uffff'));\n                this.putShort(26, (short) (overlayUV >> 16 & '\\uffff'));\n                i = 28;\n            } else {\n                i = 24;\n            }\n\n            this.putShort(i + 0, (short) (lightmapUV & '\\uffff'));\n            this.putShort(i + 2, (short) (lightmapUV >> 16 & '\\uffff'));\n            this.putByte(i + 4, BufferVertexConsumer.normalIntValue(normalX)); // @mcp: normalIntValue = normalInt\n            this.putByte(i + 5, BufferVertexConsumer.normalIntValue(normalY));\n            this.putByte(i + 6, BufferVertexConsumer.normalIntValue(normalZ));\n            this.nextElementBytes += i + 8;\n            this.endVertex();\n        } else {\n            super.vertex(x, y, z, red, green, blue, alpha, texU, texV, overlayUV, lightmapUV, normalX, normalY, normalZ);\n        }\n    }\n\n    public Pair<DireBufferBuilder.DrawState, ByteBuffer> getNextBuffer() {\n        DireBufferBuilder.DrawState bufferbuilder$drawstate = this.drawStates.get(this.drawStateIndex++);\n        this.byteBuffer.position(this.uploadedBytes);\n        this.uploadedBytes += bufferbuilder$drawstate.getVertexCount() * bufferbuilder$drawstate.getFormat().getVertexSize();\n        this.byteBuffer.limit(this.uploadedBytes);\n        if (this.drawStateIndex == this.drawStates.size() && this.vertexCount == 0) {\n            this.reset();\n        }\n\n        ByteBuffer bytebuffer = this.byteBuffer.slice();\n        this.byteBuffer.clear();\n        return Pair.of(bufferbuilder$drawstate, bytebuffer);\n    }\n\n    public void reset() {\n        if (this.renderedBytes != this.uploadedBytes) {\n            LOGGER.warn(\"Bytes mismatch \" + this.renderedBytes + \" \" + this.uploadedBytes);\n        }\n\n        this.discard();\n    }\n\n    public void discard() {\n        this.renderedBytes = 0;\n        this.uploadedBytes = 0;\n        this.nextElementBytes = 0;\n        this.drawStates.clear();\n        this.drawStateIndex = 0;\n    }\n\n    public VertexFormatElement currentElement() {\n        if (this.vertexFormatElement == null) {\n            throw new IllegalStateException(\"BufferBuilder not started\");\n        } else {\n            return this.vertexFormatElement;\n        }\n    }\n\n    public boolean isDrawing() {\n        return this.isDrawing;\n    }\n\n    @OnlyIn(Dist.CLIENT)\n    public static final class DrawState {\n        private final VertexFormat format;\n        private final int vertexCount;\n        private final int drawMode;\n\n        private DrawState(VertexFormat formatIn, int vertexCountIn, int drawModeIn) {\n            this.format = formatIn;\n            this.vertexCount = vertexCountIn;\n            this.drawMode = drawModeIn;\n        }\n\n        public VertexFormat getFormat() {\n            return this.format;\n        }\n\n        public int getVertexCount() {\n            return this.vertexCount;\n        }\n\n        public int getDrawMode() {\n            return this.drawMode;\n        }\n    }\n\n    @OnlyIn(Dist.CLIENT)\n    public static class State {\n        private final ByteBuffer stateByteBuffer;\n        private final VertexFormat stateVertexFormat;\n\n        private State(ByteBuffer byteBufferIn, VertexFormat vertexFormatIn) {\n            this.stateByteBuffer = byteBufferIn;\n            this.stateVertexFormat = vertexFormatIn;\n        }\n    }\n\n    public void putBulkData(ByteBuffer buffer) {\n        growBuffer(buffer.limit() + this.vertexFormat.getVertexSize());\n        this.byteBuffer.position(this.vertexCount * this.vertexFormat.getVertexSize());\n        this.byteBuffer.put(buffer);\n        this.vertexCount += buffer.limit() / this.vertexFormat.getVertexSize();\n    }\n\n    // Forge start\n    public VertexFormat getVertexFormat() {\n        return this.vertexFormat;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/client/renderer/DireVertexBuffer.java",
    "content": "package com.direwolf20.buildinggadgets.client.renderer;\n\nimport com.mojang.blaze3d.systems.RenderSystem;\nimport com.mojang.blaze3d.vertex.VertexFormat;\nimport com.mojang.datafixers.util.Pair;\nimport org.joml.Matrix4f;\n\nimport java.nio.ByteBuffer;\nimport java.util.concurrent.CompletableFuture;\n\npublic class DireVertexBuffer implements AutoCloseable {\n\n    private int glBufferId;\n    private final VertexFormat vertexFormat;\n    private int count;\n\n    public DireVertexBuffer(VertexFormat vertexFormatIn) {\n        this.vertexFormat = vertexFormatIn;\n        RenderSystem.glGenBuffers((p_227876_1_) -> {\n            this.glBufferId = p_227876_1_;\n        });\n    }\n\n    public void bindBuffer() {\n        RenderSystem.glBindBuffer(34962, () -> this.glBufferId);\n    }\n\n    public void upload(DireBufferBuilder bufferIn) {\n        if (!RenderSystem.isOnRenderThread()) {\n            RenderSystem.recordRenderCall(() -> {\n                this.uploadRaw(bufferIn);\n            });\n        } else {\n            this.uploadRaw(bufferIn);\n        }\n\n    }\n\n    public CompletableFuture<Void> uploadLater(DireBufferBuilder bufferIn) {\n        if (!RenderSystem.isOnRenderThread()) {\n            return CompletableFuture.runAsync(() -> {\n                this.uploadRaw(bufferIn);\n            }, (p_227877_0_) -> {\n                RenderSystem.recordRenderCall(p_227877_0_::run);\n            });\n        } else {\n            this.uploadRaw(bufferIn);\n            return CompletableFuture.completedFuture((Void) null);\n        }\n    }\n\n    private void uploadRaw(DireBufferBuilder bufferIn) {\n        Pair<DireBufferBuilder.DrawState, ByteBuffer> pair = bufferIn.getNextBuffer();\n        if (this.glBufferId != -1) {\n            ByteBuffer bytebuffer = pair.getSecond();\n            this.count = bytebuffer.remaining() / this.vertexFormat.getVertexSize();\n            this.bindBuffer();\n            RenderSystem.glBufferData(34962, bytebuffer, 35044);\n            unbindBuffer();\n        }\n    }\n\n    public void draw(Matrix4f matrixIn, int modeIn) {\n//        Render\n//        RenderSystem.pushMatrix();\n//        RenderSystem.loadIdentity();\n//        RenderSystem.multMatrix(matrixIn);\n//        RenderSystem.drawArrays(modeIn, 0, this.count);\n//        RenderSystem.popMatrix();\n    }\n\n    public static void unbindBuffer() {\n        RenderSystem.glBindBuffer(34962, () -> 0);\n    }\n\n    public void close() {\n        if (this.glBufferId >= 0) {\n            RenderSystem.glDeleteBuffers(this.glBufferId);\n            this.glBufferId = -1;\n        }\n\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/client/renderer/EffectBlockTER.java",
    "content": "package com.direwolf20.buildinggadgets.client.renderer;\n\nimport com.direwolf20.buildinggadgets.common.blocks.EffectBlock;\nimport com.direwolf20.buildinggadgets.common.blocks.OurBlocks;\nimport com.direwolf20.buildinggadgets.common.tainted.building.BlockData;\nimport com.direwolf20.buildinggadgets.common.tileentities.EffectBlockTileEntity;\nimport com.mojang.blaze3d.vertex.PoseStack;\nimport com.mojang.blaze3d.vertex.VertexConsumer;\nimport net.minecraft.client.Minecraft;\nimport net.minecraft.client.renderer.MultiBufferSource;\nimport net.minecraft.client.renderer.RenderType;\nimport net.minecraft.client.renderer.block.BlockRenderDispatcher;\nimport net.minecraft.client.renderer.blockentity.BlockEntityRenderer;\nimport net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;\nimport net.minecraft.client.renderer.texture.OverlayTexture;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraftforge.client.model.data.ModelData;\nimport org.joml.Matrix4f;\n\npublic class EffectBlockTER implements BlockEntityRenderer<EffectBlockTileEntity> {\n\n    public EffectBlockTER(BlockEntityRendererProvider.Context p_173540_) {\n    }\n\n    @Override\n    public void render(EffectBlockTileEntity tile, float partialTicks, PoseStack stack, MultiBufferSource buffer, int combinedLightIn, int combinedOverlayIn) {\n        BlockData renderData = tile.getRenderedBlock();\n        if (renderData == null)\n            return;\n        VertexConsumer builder;\n\n        MultiBufferSource.BufferSource buffer2 = Minecraft.getInstance().renderBuffers().bufferSource();\n        EffectBlock.Mode toolMode = tile.getReplacementMode();\n        BlockRenderDispatcher dispatcher = Minecraft.getInstance().getBlockRenderer();\n\n        int teCounter = tile.getTicksExisted();\n        int maxLife = tile.getLifespan();\n        teCounter = Math.min(teCounter, maxLife);\n\n        float scale = (float) (teCounter) / (float) maxLife;\n        if (scale >= 1.0f)\n            scale = 0.99f;\n        if (toolMode == EffectBlock.Mode.REMOVE || toolMode == EffectBlock.Mode.REPLACE)\n            scale = (float) (maxLife - teCounter) / maxLife;\n\n        float trans = (1 - scale) / 2;\n\n        stack.pushPose();\n        stack.translate(trans, trans, trans);\n        stack.scale(scale, scale, scale);\n\n        BlockState renderBlockState = renderData.getState();\n\n        if (tile.isUsingPaste() && toolMode == EffectBlock.Mode.PLACE)\n            renderBlockState = OurBlocks.CONSTRUCTION_DENSE_BLOCK.get().defaultBlockState();\n\n        OurRenderTypes.MultiplyAlphaRenderTypeBuffer mutatedBuffer = new OurRenderTypes.MultiplyAlphaRenderTypeBuffer(Minecraft.getInstance().renderBuffers().bufferSource(), .55f);\n        try {\n            dispatcher.renderSingleBlock(\n                    renderBlockState, stack, mutatedBuffer, 15728640, OverlayTexture.NO_OVERLAY, ModelData.EMPTY, RenderType.solid()\n            );\n        } catch (Exception ignored) {} // if it fails to render then we'll get a bug report I'm sure.\n\n        stack.popPose();\n        stack.pushPose();\n\n        builder = buffer.getBuffer(OurRenderTypes.MissingBlockOverlay);\n\n        float x = 0,\n                y = 0,\n                z = 0,\n                maxX = 1,\n                maxY = 1,\n                maxZ = 1,\n                red = 0f,\n                green = 1f,\n                blue = 1f;\n\n        if (toolMode == EffectBlock.Mode.REMOVE || toolMode == EffectBlock.Mode.REPLACE) {\n            red = 1f;\n            green = 0.25f;\n            blue = 0.25f;\n        }\n\n        float alpha = (1f - (scale));\n        if (alpha < 0.051f)\n            alpha = 0.051f;\n\n        if (alpha > 0.33f)\n            alpha = 0.33f;\n\n        Matrix4f matrix = stack.last().pose();\n\n        // Down\n        if (tile.getLevel().getBlockState(tile.getBlockPos().below()).getBlock() != OurBlocks.EFFECT_BLOCK.get()) {\n            builder.vertex(matrix, x, y, z).color(red, green, blue, alpha).endVertex();\n            builder.vertex(matrix, maxX, y, z).color(red, green, blue, alpha).endVertex();\n            builder.vertex(matrix, maxX, y, maxZ).color(red, green, blue, alpha).endVertex();\n            builder.vertex(matrix, x, y, maxZ).color(red, green, blue, alpha).endVertex();\n        }\n        // Up\n        if (tile.getLevel().getBlockState(tile.getBlockPos().above()).getBlock() != OurBlocks.EFFECT_BLOCK.get()) {\n            builder.vertex(matrix, x, maxY, z).color(red, green, blue, alpha).endVertex();\n            builder.vertex(matrix, x, maxY, maxZ).color(red, green, blue, alpha).endVertex();\n            builder.vertex(matrix, maxX, maxY, maxZ).color(red, green, blue, alpha).endVertex();\n            builder.vertex(matrix, maxX, maxY, z).color(red, green, blue, alpha).endVertex();\n        }\n        // North\n        if (tile.getLevel().getBlockState(tile.getBlockPos().north()).getBlock() != OurBlocks.EFFECT_BLOCK.get()) {\n            builder.vertex(matrix, x, y, z).color(red, green, blue, alpha).endVertex();\n            builder.vertex(matrix, x, maxY, z).color(red, green, blue, alpha).endVertex();\n            builder.vertex(matrix, maxX, maxY, z).color(red, green, blue, alpha).endVertex();\n            builder.vertex(matrix, maxX, y, z).color(red, green, blue, alpha).endVertex();\n        }\n        // South\n        if (tile.getLevel().getBlockState(tile.getBlockPos().south()).getBlock() != OurBlocks.EFFECT_BLOCK.get()) {\n            builder.vertex(matrix, x, y, maxZ).color(red, green, blue, alpha).endVertex();\n            builder.vertex(matrix, maxX, y, maxZ).color(red, green, blue, alpha).endVertex();\n            builder.vertex(matrix, maxX, maxY, maxZ).color(red, green, blue, alpha).endVertex();\n            builder.vertex(matrix, x, maxY, maxZ).color(red, green, blue, alpha).endVertex();\n        }\n        // East\n        if (tile.getLevel().getBlockState(tile.getBlockPos().east()).getBlock() != OurBlocks.EFFECT_BLOCK.get()) {\n            builder.vertex(matrix, maxX, y, z).color(red, green, blue, alpha).endVertex();\n            builder.vertex(matrix, maxX, maxY, z).color(red, green, blue, alpha).endVertex();\n            builder.vertex(matrix, maxX, maxY, maxZ).color(red, green, blue, alpha).endVertex();\n            builder.vertex(matrix, maxX, y, maxZ).color(red, green, blue, alpha).endVertex();\n        }\n        // West\n        if (tile.getLevel().getBlockState(tile.getBlockPos().west()).getBlock() != OurBlocks.EFFECT_BLOCK.get()) {\n            builder.vertex(matrix, x, y, z).color(red, green, blue, alpha).endVertex();\n            builder.vertex(matrix, x, y, maxZ).color(red, green, blue, alpha).endVertex();\n            builder.vertex(matrix, x, maxY, maxZ).color(red, green, blue, alpha).endVertex();\n            builder.vertex(matrix, x, maxY, z).color(red, green, blue, alpha).endVertex();\n        }\n        stack.popPose();\n        buffer2.endBatch(); // @mcp: draw (yarn) = finish (mcp)\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/client/renderer/MyRenderMethods.java",
    "content": "package com.direwolf20.buildinggadgets.client.renderer;\n\nimport com.mojang.blaze3d.vertex.PoseStack;\nimport com.mojang.blaze3d.vertex.VertexConsumer;\nimport net.minecraft.client.renderer.block.model.BakedQuad;\n\nimport java.util.List;\n\npublic class MyRenderMethods {\n    // TODO: Replace with native method\n    public static void renderModelBrightnessColorQuads(PoseStack.Pose matrixEntry, VertexConsumer builder, float red, float green, float blue, float alpha, List<BakedQuad> listQuads, int combinedLightsIn, int combinedOverlayIn) {\n        for(BakedQuad bakedquad : listQuads) {\n            float f;\n            float f1;\n            float f2;\n\n            if (bakedquad.isTinted()) {\n                f = red * 1f;\n                f1 = green * 1f;\n                f2 = blue * 1f;\n            } else {\n                f = 1f;\n                f1 = 1f;\n                f2 = 1f;\n            }\n\n            builder.putBulkData(matrixEntry, bakedquad, f, f1, f2, alpha, combinedLightsIn, combinedOverlayIn, true);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/client/renderer/OurRenderTypes.java",
    "content": "package com.direwolf20.buildinggadgets.client.renderer;\n\n\nimport com.mojang.blaze3d.vertex.DefaultVertexFormat;\nimport com.mojang.blaze3d.vertex.VertexConsumer;\nimport com.mojang.blaze3d.vertex.VertexFormat;\nimport net.minecraft.client.renderer.MultiBufferSource;\nimport net.minecraft.client.renderer.RenderStateShard;\nimport net.minecraft.client.renderer.RenderType;\nimport net.minecraft.client.renderer.Sheets;\nimport net.minecraft.resources.ResourceLocation;\nimport net.minecraft.world.inventory.InventoryMenu;\nimport org.joml.Matrix3f;\nimport org.joml.Matrix4f;\n\nimport java.util.OptionalDouble;\n\n\npublic class OurRenderTypes extends RenderType {\n\n\n    private static final LineStateShard THICK_LINES = new LineStateShard(OptionalDouble.of(3.0D));\n\n    public static final RenderType RenderBlock = create(\"GadgetRenderBlock\",\n            DefaultVertexFormat.BLOCK, VertexFormat.Mode.QUADS, 256, false, false,\n            RenderType.CompositeState.builder()\n//                    .setShadeModelState(SMOOTH_SHADE)\n                    .setShaderState(RenderStateShard.BLOCK_SHADER)\n                    .setLightmapState(LIGHTMAP)\n                    .setTextureState(BLOCK_SHEET_MIPPED) //BLOCK_SHEET_MIPPED (mcp) = BLOCK_SHEET_MIPPED (yarn)\n                    .setLayeringState(VIEW_OFFSET_Z_LAYERING) // view_offset_z_layering\n                    .setTransparencyState(TRANSLUCENT_TRANSPARENCY)\n                    .setDepthTestState(LEQUAL_DEPTH_TEST)\n                    .setCullState(NO_CULL)\n                    .setWriteMaskState(COLOR_DEPTH_WRITE)\n                    .createCompositeState(false));\n\n    public static final RenderType MissingBlockOverlay = create(\"GadgetMissingBlockOverlay\",\n            DefaultVertexFormat.POSITION_COLOR, VertexFormat.Mode.QUADS, 256, false, false,\n            RenderType.CompositeState.builder()\n                    .setShaderState(RenderStateShard.POSITION_COLOR_SHADER)\n                    .setLayeringState(VIEW_OFFSET_Z_LAYERING) // view_offset_z_layering\n                    .setTransparencyState(TRANSLUCENT_TRANSPARENCY)\n                    .setTextureState(NO_TEXTURE)\n                    .setDepthTestState(LEQUAL_DEPTH_TEST)\n                    .setCullState(NO_CULL)\n                    .setLightmapState(NO_LIGHTMAP)\n                    .setWriteMaskState(COLOR_WRITE)\n                    .createCompositeState(false));\n\n//    public static final RenderType.CompositeRenderType LINES =\n//            create(\"lines\", DefaultVertexFormat.POSITION_COLOR_NORMAL, VertexFormat.Mode.LINES, 256, RenderType.CompositeState.builder()\n//            .setShaderState(RENDERTYPE_LINES_SHADER)\n//            .setLineState(new RenderStateShard.LineStateShard(OptionalDouble.empty()))\n//            .setLayeringState(VIEW_OFFSET_Z_LAYERING)\n//            .setTransparencyState(TRANSLUCENT_TRANSPARENCY)\n//            .setOutputState(ITEM_ENTITY_TARGET)\n//            .setWriteMaskState(COLOR_DEPTH_WRITE)\n//            .setCullState(NO_CULL)\n//            .createCompositeState(false));\n\n    public static final RenderType CopyGadgetLines = create(\"GadgetCopyLines\",\n            DefaultVertexFormat.POSITION_COLOR, VertexFormat.Mode.LINES, 256, false, false,\n            RenderType.CompositeState.builder()\n                    .setShaderState(RENDERTYPE_LINES_SHADER)\n                    .setLineState(new LineStateShard(OptionalDouble.of(2.0D)))\n                    .setLayeringState(VIEW_OFFSET_Z_LAYERING)\n                    .setTransparencyState(TRANSLUCENT_TRANSPARENCY)\n                    .setOutputState(ITEM_ENTITY_TARGET)\n                    .setCullState(NO_CULL)\n                    .setWriteMaskState(COLOR_DEPTH_WRITE)\n                    .createCompositeState(false));\n\n\n    public static final RenderType CopyPasteRenderBlock = create(\"CopyPasteRenderBlock\",\n            DefaultVertexFormat.BLOCK, VertexFormat.Mode.QUADS, 256, false, false,\n            RenderType.CompositeState.builder()\n                    .setShaderState(RenderStateShard.BLOCK_SHADER)\n//                    .setShadeModelState(SMOOTH_SHADE)\n                    .setLightmapState(LIGHTMAP)\n                    .setTextureState(BLOCK_SHEET_MIPPED) //BLOCK_SHEET_MIPPED (mcp) = BLOCK_SHEET_MIPPED (yarn)\n                    .setLayeringState(VIEW_OFFSET_Z_LAYERING) // view_offset_z_layering\n                    .setTransparencyState(TRANSLUCENT_TRANSPARENCY)\n                    .setDepthTestState(LEQUAL_DEPTH_TEST)\n                    .setCullState(NO_CULL)\n                    .setWriteMaskState(COLOR_WRITE)\n                    .createCompositeState(false));\n\n    public static final RenderType BlockOverlay = create(\"BGBlockOverlay\",\n            DefaultVertexFormat.POSITION_COLOR, VertexFormat.Mode.QUADS, 256, false, false,\n            RenderType.CompositeState.builder()\n                    .setShaderState(RenderStateShard.RENDERTYPE_SOLID_SHADER)\n                    .setLayeringState(VIEW_OFFSET_Z_LAYERING) // view_offset_z_layering\n                    .setTransparencyState(TRANSLUCENT_TRANSPARENCY)\n                    .setTextureState(NO_TEXTURE)\n                    .setDepthTestState(LEQUAL_DEPTH_TEST)\n                    .setCullState(CULL)\n                    .setLightmapState(NO_LIGHTMAP)\n                    .setWriteMaskState(COLOR_DEPTH_WRITE)\n                    .createCompositeState(false));\n\n    public static final RenderType TRIANGLE_STRIP =\n            create(\"triangle_strip\", DefaultVertexFormat.POSITION_COLOR, VertexFormat.Mode.TRIANGLE_STRIP, 256, false, false,\n                    RenderType.CompositeState.builder()\n                            .setShaderState(RenderStateShard.POSITION_COLOR_SHADER)\n                            .setTextureState(NO_TEXTURE)\n                            .setTransparencyState(TRANSLUCENT_TRANSPARENCY)\n                            .setCullState(NO_CULL)\n                            .setLightmapState(NO_LIGHTMAP)\n                            .createCompositeState(false));\n\n    public OurRenderTypes(String p_173178_, VertexFormat p_173179_, VertexFormat.Mode p_173180_, int p_173181_, boolean p_173182_, boolean p_173183_, Runnable p_173184_, Runnable p_173185_) {\n        super(p_173178_, p_173179_, p_173180_, p_173181_, p_173182_, p_173183_, p_173184_, p_173185_);\n    }\n\n    /**\n     * This is used for rendering blocks with an alpha value as the alpha currently isn't\n     * supported by minecraft.\n     * <p>\n     * Literally just raps the buffer so we can render a different RenderType\n     */\n    public static class MultiplyAlphaRenderTypeBuffer implements MultiBufferSource {\n        private final MultiBufferSource inner;\n        private final float constantAlpha;\n\n        public MultiplyAlphaRenderTypeBuffer(MultiBufferSource inner, float constantAlpha) {\n            this.inner = inner;\n            this.constantAlpha = constantAlpha;\n        }\n\n        @Override\n        public VertexConsumer getBuffer(RenderType type) {\n            RenderType localType = type;\n            if (localType instanceof CompositeRenderType) {\n                // all of this requires a lot of AT's so be aware of that on ports\n                ResourceLocation texture = ((TextureStateShard) ((CompositeRenderType) localType).state.textureState).texture\n                        .orElse(InventoryMenu.BLOCK_ATLAS);\n\n                localType = entityTranslucentCull(texture);\n            } else if (localType.toString().equals(Sheets.translucentCullBlockSheet().toString())) {\n                localType = Sheets.translucentCullBlockSheet();\n            }\n\n            return new MultiplyAlphaVertexBuilder(this.inner.getBuffer(localType), this.constantAlpha);\n        }\n\n        /**\n         * Required for modifying the alpha value.\n         */\n        public static class MultiplyAlphaVertexBuilder implements VertexConsumer {\n            private final VertexConsumer inner;\n            private final float constantAlpha;\n\n            public MultiplyAlphaVertexBuilder(VertexConsumer inner, float constantAlpha) {\n                this.inner = inner;\n                this.constantAlpha = constantAlpha;\n            }\n\n            @Override\n            public VertexConsumer vertex(double x, double y, double z) {\n                return inner.vertex(x, y, z);\n            }\n\n            @Override\n            public VertexConsumer vertex(Matrix4f matrixIn, float x, float y, float z) {\n                return inner.vertex(matrixIn, x, y, z);\n            }\n\n            @Override\n            public VertexConsumer color(int red, int green, int blue, int alpha) {\n                return inner.color(red, green, blue, (int) (alpha * constantAlpha));\n            }\n\n            @Override\n            public VertexConsumer uv(float u, float v) {\n                return inner.uv(u, v);\n            }\n\n            @Override\n            public VertexConsumer overlayCoords(int u, int v) {\n                return inner.overlayCoords(u, v);\n            }\n\n\n            @Override\n            public VertexConsumer uv2(int u, int v) {\n                return inner.uv2(u, v);\n            }\n\n            @Override\n            public VertexConsumer normal(float x, float y, float z) {\n                return inner.normal(x, y, z);\n            }\n\n            @Override\n            public VertexConsumer normal(Matrix3f matrixIn, float x, float y, float z) {\n                return inner.normal(matrixIn, x, y, z);\n            }\n\n            @Override\n            public void endVertex() {\n                this.inner.endVertex();\n            }\n\n            @Override\n            public void defaultColor(int p_166901_, int p_166902_, int p_166903_, int p_166904_) {\n                inner.defaultColor(p_166901_, p_166902_, p_166903_, p_166904_);\n            }\n\n            @Override\n            public void unsetDefaultColor() {\n                inner.unsetDefaultColor();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/client/renderer/package-info.java",
    "content": "@ParametersAreNonnullByDefault\n@MethodsReturnNonnullByDefault\npackage com.direwolf20.buildinggadgets.client.renderer;\n\nimport net.minecraft.MethodsReturnNonnullByDefault;\n\nimport javax.annotation.ParametersAreNonnullByDefault;"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/client/renders/BaseRenderer.java",
    "content": "package com.direwolf20.buildinggadgets.client.renders;\n\nimport com.direwolf20.buildinggadgets.client.cache.RemoteInventoryCache;\nimport com.direwolf20.buildinggadgets.client.renderer.OurRenderTypes;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.InventoryLinker;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.materials.objects.UniqueItem;\nimport com.direwolf20.buildinggadgets.common.world.MockBuilderWorld;\nimport com.direwolf20.buildinggadgets.common.world.MockTileEntityRenderWorld;\nimport com.google.common.collect.Multiset;\nimport com.mojang.blaze3d.systems.RenderSystem;\nimport com.mojang.blaze3d.vertex.PoseStack;\nimport com.mojang.blaze3d.vertex.VertexConsumer;\nimport net.minecraft.client.Minecraft;\nimport net.minecraft.client.renderer.MultiBufferSource;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.resources.ResourceKey;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.inventory.InventoryMenu;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.Blocks;\nimport net.minecraft.world.level.block.entity.BlockEntity;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.phys.Vec3;\nimport net.minecraftforge.client.event.RenderLevelStageEvent;\nimport net.minecraftforge.common.capabilities.ForgeCapabilities;\nimport net.minecraftforge.common.util.LazyOptional;\nimport net.minecraftforge.energy.IEnergyStorage;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.joml.Matrix4f;\n\nimport java.util.HashSet;\nimport java.util.Set;\n\npublic abstract class BaseRenderer {\n    public static final BlockState AIR = Blocks.AIR.defaultBlockState();\n\n    private static final MockTileEntityRenderWorld tileEntityWorld = new MockTileEntityRenderWorld();\n    private static final MockBuilderWorld builderWorld = new MockBuilderWorld();\n    private static final Set<BlockEntity> invalidTileEntities = new HashSet<>();\n\n    private static final RemoteInventoryCache cacheInventory = new RemoteInventoryCache(false);\n\n    public void render(RenderLevelStageEvent evt, Player player, ItemStack heldItem) {\n        // FIXME: might be wrong\n        if (evt.getStage() != RenderLevelStageEvent.Stage.AFTER_TRANSLUCENT_BLOCKS) {\n            return;\n        }\n\n        // This is necessary to prevent issues with not rendering the overlay's at all (when Botania being present) - See #329 for more information\n        bindBlocks();\n\n        if( this.isLinkable() )\n            BaseRenderer.renderLinkedInventoryOutline(evt, heldItem, player);\n    }\n\n    private void bindBlocks() {\n        RenderSystem.setShaderTexture(0, InventoryMenu.BLOCK_ATLAS);\n    }\n\n    private static void renderLinkedInventoryOutline(RenderLevelStageEvent evt, ItemStack item, Player player) {\n        Pair<BlockPos, ResourceKey<Level>> dataFromStack = InventoryLinker.getDataFromStack(item);\n        if (dataFromStack == null) {\n            return;\n        }\n\n        if (!player.level.dimension().equals(dataFromStack.getValue())) {\n            return;\n        }\n\n        BlockPos pos = dataFromStack.getKey();\n        Vec3 renderPos = getMc().gameRenderer.getMainCamera().getPosition()\n                .subtract(pos.getX(), pos.getY(), pos.getZ())\n                .add(.005f, .005f, .005f);\n\n        MultiBufferSource.BufferSource buffer = Minecraft.getInstance().renderBuffers().bufferSource();\n\n        PoseStack stack = evt.getPoseStack();\n        stack.pushPose();\n        stack.translate(-renderPos.x(), -renderPos.y(), -renderPos.z());\n        stack.scale(1.01f, 1.01f, 1.01f);\n\n        renderBoxSolid(stack.last().pose(), buffer.getBuffer(OurRenderTypes.BlockOverlay), BlockPos.ZERO, 0, 1, 0, .35f);\n\n        stack.popPose();\n        RenderSystem.disableDepthTest();\n        buffer.endBatch(); // @mcp: finish (mcp) = draw (yarn)\n    }\n\n\n    int getEnergy(Player player, ItemStack heldItem) {\n        LazyOptional<IEnergyStorage> energy = heldItem.getCapability(ForgeCapabilities.ENERGY);\n        if (player.isCreative() || !energy.isPresent())\n            return Integer.MAX_VALUE;\n\n        return energy.map(IEnergyStorage::getEnergyStored).orElse(0);\n    }\n\n    protected static void renderMissingBlock(Matrix4f matrix, VertexConsumer builder, BlockPos pos) {\n        renderBoxSolid(matrix, builder, pos, 1f, 0f, 0f, 0.35f);\n    }\n\n    protected static void renderBoxSolid(Matrix4f matrix, VertexConsumer builder, BlockPos pos, float r, float g, float b, float alpha) {\n        double x = pos.getX() - 0.001;\n        double y = pos.getY() - 0.001;\n        double z = pos.getZ() - 0.001;\n        double xEnd = pos.getX() + 1.0015;\n        double yEnd = pos.getY() + 1.0015;\n        double zEnd = pos.getZ() + 1.0015;\n\n        renderBoxSolid(matrix, builder, x, y, z, xEnd, yEnd, zEnd, r, g, b, alpha);\n    }\n\n    protected static void renderBoxSolid(Matrix4f matrix, VertexConsumer builder, double x, double y, double z, double xEnd, double yEnd, double zEnd, float red, float green, float blue, float alpha) {\n        //careful: mc want's it's vertices to be defined CCW - if you do it the other way around weird cullling issues will arise\n        //CCW herby counts as if you were looking at it from the outside\n        float startX = (float) x;\n        float startY = (float) y;\n        float startZ = (float) z;\n        float endX = (float) xEnd;\n        float endY = (float) yEnd;\n        float endZ = (float) zEnd;\n\n//        float startX = 0, startY = 0, startZ = -1, endX = 1, endY = 1, endZ = 0;\n\n        //down\n        builder.vertex(matrix, startX, startY, startZ).color(red, green, blue, alpha).endVertex();\n        builder.vertex(matrix, endX, startY, startZ).color(red, green, blue, alpha).endVertex();\n        builder.vertex(matrix, endX, startY, endZ).color(red, green, blue, alpha).endVertex();\n        builder.vertex(matrix, startX, startY, endZ).color(red, green, blue, alpha).endVertex();\n\n        //up\n        builder.vertex(matrix, startX, endY, startZ).color(red, green, blue, alpha).endVertex();\n        builder.vertex(matrix, startX, endY, endZ).color(red, green, blue, alpha).endVertex();\n        builder.vertex(matrix, endX, endY, endZ).color(red, green, blue, alpha).endVertex();\n        builder.vertex(matrix, endX, endY, startZ).color(red, green, blue, alpha).endVertex();\n\n        //east\n        builder.vertex(matrix, startX, startY, startZ).color(red, green, blue, alpha).endVertex();\n        builder.vertex(matrix, startX, endY, startZ).color(red, green, blue, alpha).endVertex();\n        builder.vertex(matrix, endX, endY, startZ).color(red, green, blue, alpha).endVertex();\n        builder.vertex(matrix, endX, startY, startZ).color(red, green, blue, alpha).endVertex();\n\n        //west\n        builder.vertex(matrix, startX, startY, endZ).color(red, green, blue, alpha).endVertex();\n        builder.vertex(matrix, endX, startY, endZ).color(red, green, blue, alpha).endVertex();\n        builder.vertex(matrix, endX, endY, endZ).color(red, green, blue, alpha).endVertex();\n        builder.vertex(matrix, startX, endY, endZ).color(red, green, blue, alpha).endVertex();\n\n        //south\n        builder.vertex(matrix, endX, startY, startZ).color(red, green, blue, alpha).endVertex();\n        builder.vertex(matrix, endX, endY, startZ).color(red, green, blue, alpha).endVertex();\n        builder.vertex(matrix, endX, endY, endZ).color(red, green, blue, alpha).endVertex();\n        builder.vertex(matrix, endX, startY, endZ).color(red, green, blue, alpha).endVertex();\n\n        //north\n        builder.vertex(matrix, startX, startY, startZ).color(red, green, blue, alpha).endVertex();\n        builder.vertex(matrix, startX, startY, endZ).color(red, green, blue, alpha).endVertex();\n        builder.vertex(matrix, startX, endY, endZ).color(red, green, blue, alpha).endVertex();\n        builder.vertex(matrix, startX, endY, startZ).color(red, green, blue, alpha).endVertex();\n    }\n\n    /**\n     * Can the gadget be linked to other inventories?\n     */\n    public boolean isLinkable() {\n        return false;\n    }\n\n    static Minecraft getMc() {\n        return Minecraft.getInstance();\n    }\n\n    static MockBuilderWorld getBuilderWorld() {\n        return builderWorld;\n    }\n\n    static RemoteInventoryCache getCacheInventory() {\n        return cacheInventory;\n    }\n\n    public static void setInventoryCache(Multiset<UniqueItem> cache) {\n        BaseRenderer.cacheInventory.setCache(cache);\n    }\n\n    public static void updateInventoryCache() {\n        cacheInventory.forceUpdate();\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/client/renders/BuildRender.java",
    "content": "package com.direwolf20.buildinggadgets.client.renders;\n\nimport com.direwolf20.buildinggadgets.client.renderer.OurRenderTypes;\nimport com.direwolf20.buildinggadgets.common.blocks.OurBlocks;\nimport com.direwolf20.buildinggadgets.common.items.AbstractGadget;\nimport com.direwolf20.buildinggadgets.common.items.GadgetBuilding;\nimport com.direwolf20.buildinggadgets.common.items.GadgetExchanger;\nimport com.direwolf20.buildinggadgets.common.items.modes.AbstractMode;\nimport com.direwolf20.buildinggadgets.common.tainted.building.BlockData;\nimport com.direwolf20.buildinggadgets.common.tainted.building.view.BuildContext;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.IItemIndex;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.InventoryHelper;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.MatchResult;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.RecordingItemIndex;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.materials.MaterialList;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.materials.objects.UniqueItem;\nimport com.direwolf20.buildinggadgets.common.util.helpers.VectorHelper;\nimport com.mojang.blaze3d.systems.RenderSystem;\nimport com.mojang.blaze3d.vertex.PoseStack;\nimport com.mojang.blaze3d.vertex.VertexConsumer;\nimport net.minecraft.client.Minecraft;\nimport net.minecraft.client.renderer.MultiBufferSource;\nimport net.minecraft.client.renderer.RenderType;\nimport net.minecraft.client.renderer.block.BlockRenderDispatcher;\nimport net.minecraft.client.renderer.texture.OverlayTexture;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.phys.BlockHitResult;\nimport net.minecraft.world.phys.Vec3;\nimport net.minecraftforge.client.event.RenderLevelStageEvent;\nimport net.minecraftforge.client.model.data.ModelData;\nimport net.minecraftforge.common.capabilities.ForgeCapabilities;\nimport net.minecraftforge.common.util.LazyOptional;\nimport net.minecraftforge.energy.IEnergyStorage;\n\nimport java.util.List;\nimport java.util.Optional;\n\nimport static com.direwolf20.buildinggadgets.common.util.GadgetUtils.getAnchor;\nimport static com.direwolf20.buildinggadgets.common.util.GadgetUtils.getToolBlock;\n\npublic class BuildRender extends BaseRenderer {\n    private final boolean isExchanger;\n    private static final BlockState DEFAULT_EFFECT_BLOCK = OurBlocks.EFFECT_BLOCK.get().defaultBlockState();\n\n    public BuildRender(boolean isExchanger) {\n        this.isExchanger = isExchanger;\n    }\n\n    @Override\n    public void render(RenderLevelStageEvent evt, Player player, ItemStack heldItem) {\n        super.render(evt, player, heldItem);\n\n        BlockHitResult lookingAt = VectorHelper.getLookingAt(player, heldItem);\n\n        BlockState state = AIR;\n        Optional<List<BlockPos>> anchor = getAnchor(heldItem);\n\n        BlockState startBlock = player.level.getBlockState(lookingAt.getBlockPos());\n        if( (player.level.isEmptyBlock(lookingAt.getBlockPos()) && !anchor.isPresent()) || startBlock == DEFAULT_EFFECT_BLOCK )\n            return;\n\n        BlockData data = getToolBlock(heldItem);\n        BlockState renderBlockState = data.getState();\n        if (renderBlockState == BaseRenderer.AIR)\n            return;\n\n        // Get the coordinates from the anchor. If the anchor isn't present then build the collector.\n        List<BlockPos> coordinates = anchor.orElseGet(() -> {\n            AbstractMode mode = !this.isExchanger ? GadgetBuilding.getToolMode(heldItem).getMode() : GadgetExchanger.getToolMode(heldItem).getMode();\n            return mode.getCollection(\n                    new AbstractMode.UseContext(player.level, player, renderBlockState, lookingAt.getBlockPos(), heldItem, lookingAt.getDirection(), !this.isExchanger && GadgetBuilding.shouldPlaceAtop(heldItem), !this.isExchanger ? GadgetBuilding.getConnectedArea(heldItem) : GadgetExchanger.getConnectedArea(heldItem)),\n                    player\n            );\n        });\n\n        // Sort them on a new line for readability\n//        coordinates = SortingHelper.Blocks.byDistance(coordinates, player);\n\n        //Prepare the fake world -- using a fake world lets us render things properly, like fences connecting.\n        getBuilderWorld().setWorldAndState(player.level, renderBlockState, coordinates);\n\n        Vec3 playerPos = getMc().gameRenderer.getMainCamera().getPosition();\n        MultiBufferSource.BufferSource buffer = Minecraft.getInstance().renderBuffers().bufferSource();\n\n        //Save the current position that is being rendered (I think)\n        PoseStack matrix = evt.getPoseStack();\n        matrix.pushPose();\n        matrix.translate(-playerPos.x(), -playerPos.y(), -playerPos.z());\n\n        BlockRenderDispatcher dispatcher = getMc().getBlockRenderer();\n\n        for (BlockPos coordinate : coordinates) {\n            matrix.pushPose();\n            matrix.translate(coordinate.getX(), coordinate.getY(), coordinate.getZ());\n            if( this.isExchanger ) {\n                matrix.translate(-0.0005f, -0.0005f, -0.0005f);\n                matrix.scale(1.001f, 1.001f, 1.001f);\n            }\n\n            // todo: add back from 1.16 port\n//            if (getBuilderWorld().getWorldType() != WorldType.DEBUG_ALL_BLOCK_STATES) { //Get the block state in the fake world\n//                try {\n                    state = renderBlockState;\n//                } catch (Exception ignored) {}\n//            }\n\n            OurRenderTypes.MultiplyAlphaRenderTypeBuffer mutatedBuffer = new OurRenderTypes.MultiplyAlphaRenderTypeBuffer(Minecraft.getInstance().renderBuffers().bufferSource(), .55f);\n            try {\n                dispatcher.renderSingleBlock(\n                        state, matrix, mutatedBuffer, 15728640, OverlayTexture.NO_OVERLAY, ModelData.EMPTY, RenderType.solid()\n                );\n            } catch (Exception ignored) {} // I'm sure if this is an issue someone will report it\n\n            //Move the render position back to where it was\n            matrix.popPose();\n            RenderSystem.disableDepthTest();\n            buffer.endBatch(); // @mcp: finish (mcp) = draw (yarn)\n        }\n\n        // Don't even waste the time checking to see if we have the right energy, items, etc for creative mode\n        if (!player.isCreative()) {\n            VertexConsumer builder;\n\n            boolean hasLinkedInventory = getCacheInventory().maintainCache(heldItem);\n            int remainingCached = getCacheInventory().getCache() == null ? -1 : getCacheInventory().getCache().count(new UniqueItem(data.getState().getBlock().asItem()));\n\n            // Figure out how many of the block we're rendering we have in the inventory of the player.\n            IItemIndex index = new RecordingItemIndex(InventoryHelper.index(heldItem, player));\n            BuildContext context = new BuildContext(player.level, player, heldItem);\n\n            MaterialList materials = data.getRequiredItems(context, null, null);\n            int hasEnergy = getEnergy(player, heldItem);\n\n            LazyOptional<IEnergyStorage> energyCap = heldItem.getCapability(ForgeCapabilities.ENERGY);\n\n            for (BlockPos coordinate : coordinates) { //Now run through the UNSORTED list of coords, to show which blocks won't place if you don't have enough of them.\n                boolean renderFree = false;\n                if (energyCap.isPresent())\n                    hasEnergy -= ((AbstractGadget) heldItem.getItem()).getEnergyCost(heldItem);\n\n                builder = buffer.getBuffer(OurRenderTypes.MissingBlockOverlay);\n                MatchResult match = index.tryMatch(materials);\n                if (!match.isSuccess())\n                    match = index.tryMatch(InventoryHelper.PASTE_LIST);\n                if (!match.isSuccess() || hasEnergy < 0) {\n                    if (hasLinkedInventory && remainingCached > 0) {\n                        renderFree = true;\n                        remainingCached --;\n                    } else {\n                        renderMissingBlock(matrix.last().pose(), builder, coordinate);\n                    }\n                } else {\n                    index.applyMatch(match); //notify the recording index that this counts\n                    renderBoxSolid(matrix.last().pose(), builder, coordinate, .97f, 1f, .99f, .1f);\n                }\n\n                if (renderFree) {\n                    renderBoxSolid(matrix.last().pose(), builder, coordinate, .97f, 1f, .99f, .1f);\n                }\n            }\n        }\n\n        matrix.popPose();\n        RenderSystem.disableDepthTest();\n        buffer.endBatch(); // @mcp: finish (mcp) = draw (yarn)\n    }\n\n    @Override\n    public boolean isLinkable() {\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/client/renders/CopyPasteRender.java",
    "content": "package com.direwolf20.buildinggadgets.client.renders;\n\nimport com.direwolf20.buildinggadgets.client.renderer.DireBufferBuilder;\nimport com.direwolf20.buildinggadgets.client.renderer.DireVertexBuffer;\nimport com.direwolf20.buildinggadgets.client.renderer.OurRenderTypes;\nimport com.direwolf20.buildinggadgets.common.capability.CapabilityTemplate;\nimport com.direwolf20.buildinggadgets.common.items.GadgetCopyPaste;\nimport com.direwolf20.buildinggadgets.common.tainted.building.PlacementTarget;\nimport com.direwolf20.buildinggadgets.common.tainted.building.Region;\nimport com.direwolf20.buildinggadgets.common.tainted.building.view.BuildContext;\nimport com.direwolf20.buildinggadgets.common.tainted.building.view.IBuildView;\nimport com.direwolf20.buildinggadgets.common.tainted.template.ITemplateKey;\nimport com.direwolf20.buildinggadgets.common.tainted.template.ITemplateProvider;\nimport com.direwolf20.buildinggadgets.common.tainted.template.ITemplateProvider.IUpdateListener;\nimport com.direwolf20.buildinggadgets.common.tainted.template.Template;\nimport com.direwolf20.buildinggadgets.common.world.MockDelegationWorld;\nimport com.google.common.collect.ImmutableMap;\nimport com.google.common.collect.Maps;\nimport com.mojang.blaze3d.vertex.PoseStack;\nimport com.mojang.blaze3d.vertex.VertexConsumer;\nimport com.mojang.blaze3d.vertex.VertexFormat;\nimport net.minecraft.client.Minecraft;\nimport net.minecraft.client.renderer.MultiBufferSource;\nimport net.minecraft.client.renderer.RenderType;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.phys.Vec3;\nimport net.minecraftforge.client.event.RenderLevelStageEvent;\nimport org.joml.Matrix3f;\nimport org.joml.Matrix4f;\n\nimport java.io.Closeable;\nimport java.util.*;\nimport java.util.function.Consumer;\n\npublic class CopyPasteRender extends BaseRenderer implements IUpdateListener {\n    private MultiVBORenderer renderBuffer;\n    private int tickTrack = 0;\n    private UUID lastRendered = null;\n\n    @Override\n    public void onTemplateUpdate(ITemplateProvider provider, ITemplateKey key, Template template) {\n        if (provider.getId(key).equals(lastRendered))\n            renderBuffer = null;\n    }\n\n    @Override\n    public void onTemplateUpdateSend(ITemplateProvider provider, ITemplateKey key, Template template) {\n        onTemplateUpdate(provider, key, template);\n    }\n\n    @Override\n    public void render(RenderLevelStageEvent evt, Player player, ItemStack heldItem) {\n        // We can completely trust that heldItem isn't empty and that it's a copy paste gadget.\n        super.render(evt, player, heldItem);\n\n        // Provide this as both renders require the data.\n        Vec3 cameraView = getMc().gameRenderer.getMainCamera().getPosition();\n\n        // translate the matric to the projected view\n        PoseStack stack = evt.getPoseStack(); //Get current matrix position from the evt call\n        stack.pushPose(); //Save the render position from RenderWorldLast\n        stack.translate(-cameraView.x(), -cameraView.y(), -cameraView.z()); //Sets render position to 0,0,0\n\n        if (GadgetCopyPaste.getToolMode(heldItem) == GadgetCopyPaste.ToolMode.COPY) {\n            renderBuffer = null; //fix the surroundings not being taken into account when you've walked around a bit\n            GadgetCopyPaste.getSelectedRegion(heldItem).ifPresent(region ->\n                    renderCopy(stack, region));\n        } else\n            renderPaste(stack, cameraView, player, heldItem);\n\n        stack.popPose();\n    }\n\n    private void renderCopy(PoseStack matrix, Region region) {\n        BlockPos startPos = region.getMin();\n        BlockPos endPos = region.getMax();\n        BlockPos blankPos = new BlockPos(0, 0, 0);\n\n        if (startPos.equals(blankPos) || endPos.equals(blankPos))\n            return;\n\n        //We want to draw from the starting position to the (ending position)+1\n        int x = Math.min(startPos.getX(), endPos.getX()), y = Math.min(startPos.getY(), endPos.getY()), z = Math.min(startPos.getZ(), endPos.getZ());\n\n        int dx = (startPos.getX() > endPos.getX()) ? startPos.getX() + 1 : endPos.getX() + 1;\n        int dy = (startPos.getY() > endPos.getY()) ? startPos.getY() + 1 : endPos.getY() + 1;\n        int dz = (startPos.getZ() > endPos.getZ()) ? startPos.getZ() + 1 : endPos.getZ() + 1;\n\n        MultiBufferSource.BufferSource buffer = Minecraft.getInstance().renderBuffers().bufferSource();\n        VertexConsumer builder = buffer.getBuffer(OurRenderTypes.lines());\n\n        matrix.pushPose();\n        Matrix4f matrix4f = matrix.last().pose();\n        Matrix3f matrix3f = matrix.last().normal();\n\n        builder.vertex(matrix4f, x, y, z).color(0F, 0F, 1F, 1.0F).normal(matrix3f, 1.0F, 0.0F, 0.0F).endVertex();\n        builder.vertex(matrix4f, dx, y, z).color(0F, 0F, 1F, 1.0F).normal(matrix3f, 1.0F, 0.0F, 0.0F).endVertex();\n        builder.vertex(matrix4f, x, y, z).color(1.0F, 1.0F, 1.0F, 1.0F).normal(matrix3f, 0.0F, 1.0F, 0.0F).endVertex();\n        builder.vertex(matrix4f, x, dy, z).color(1.0F, 1.0F, 1.0F, 1.0F).normal(matrix3f, 0.0F, 1.0F, 0.0F).endVertex();\n        builder.vertex(matrix4f, x, y, z).color(1.0F, 1.0F, 1.0F, 1.0F).normal(matrix3f, 0.0F, 0.0F, 1.0F).endVertex();\n        builder.vertex(matrix4f, x, y, dz).color(1.0F, 1.0F, 1.0F, 1.0F).normal(matrix3f, 0.0F, 0.0F, 1.0F).endVertex();\n        builder.vertex(matrix4f, dx, y, z).color(0F, 1F, 0F, 1.0F).normal(matrix3f, 0.0F, 1.0F, 0.0F).endVertex();\n        builder.vertex(matrix4f, dx, dy, z).color(0F, 1F, 0F, 1.0F).normal(matrix3f, 0.0F, 1.0F, 0.0F).endVertex();\n        builder.vertex(matrix4f, dx, dy, z).color(1.0F, 1.0F, 1.0F, 1.0F).normal(matrix3f, -1.0F, 0.0F, 0.0F).endVertex();\n        builder.vertex(matrix4f, x, dy, z).color(1.0F, 1.0F, 1.0F, 1.0F).normal(matrix3f, -1.0F, 0.0F, 0.0F).endVertex();\n        builder.vertex(matrix4f, x, dy, z).color(1.0F, 1.0F, 1.0F, 1.0F).normal(matrix3f, 0.0F, 0.0F, 1.0F).endVertex();\n        builder.vertex(matrix4f, x, dy, dz).color(1.0F, 1.0F, 1.0F, 1.0F).normal(matrix3f, 0.0F, 0.0F, 1.0F).endVertex();\n        builder.vertex(matrix4f, x, dy, dz).color(1.0F, 1.0F, 1.0F, 1.0F).normal(matrix3f, 0.0F, -1.0F, 0.0F).endVertex();\n        builder.vertex(matrix4f, x, y, dz).color(1.0F, 1.0F, 1.0F, 1.0F).normal(matrix3f, 0.0F, -1.0F, 0.0F).endVertex();\n        builder.vertex(matrix4f, x, y, dz).color(1.0F, 1.0F, 1.0F, 1.0F).normal(matrix3f, 1.0F, 0.0F, 0.0F).endVertex();\n        builder.vertex(matrix4f, dx, y, dz).color(1.0F, 1.0F, 1.0F, 1.0F).normal(matrix3f, 1.0F, 0.0F, 0.0F).endVertex();\n        builder.vertex(matrix4f, dx, y, dz).color(1F, 0F, 0F, 1.0F).normal(matrix3f, 0.0F, 0.0F, -1.0F).endVertex();\n        builder.vertex(matrix4f, dx, y, z).color(1F, 0F, 0F, 1.0F).normal(matrix3f, 0.0F, 0.0F, -1.0F).endVertex();\n        builder.vertex(matrix4f, x, dy, dz).color(1.0F, 1.0F, 1.0F, 1.0F).normal(matrix3f, 1.0F, 0.0F, 0.0F).endVertex();\n        builder.vertex(matrix4f, dx, dy, dz).color(1.0F, 1.0F, 1.0F, 1.0F).normal(matrix3f, 1.0F, 0.0F, 0.0F).endVertex();\n        builder.vertex(matrix4f, dx, y, dz).color(1.0F, 1.0F, 1.0F, 1.0F).normal(matrix3f, 0.0F, 1.0F, 0.0F).endVertex();\n        builder.vertex(matrix4f, dx, dy, dz).color(1.0F, 1.0F, 1.0F, 1.0F).normal(matrix3f, 0.0F, 1.0F, 0.0F).endVertex();\n        builder.vertex(matrix4f, dx, dy, z).color(1.0F, 1.0F, 1.0F, 1.0F).normal(matrix3f, 0.0F, 0.0F, 1.0F).endVertex();\n        builder.vertex(matrix4f, dx, dy, dz).color(1.0F, 1.0F, 1.0F, 1.0F).normal(matrix3f, 0.0F, 0.0F, 1.0F).endVertex();\n\n        buffer.endBatch(); // @mcp: draw = finish\n        matrix.popPose();\n    }\n\n    private void renderPaste(PoseStack matrices, Vec3 cameraView, Player player, ItemStack heldItem) {\n        Level world = player.level;\n\n        // Check the template cap from the world\n        // Fetch the template key (because for some reason this is it's own cap)\n        world.getCapability(CapabilityTemplate.TEMPLATE_PROVIDER_CAPABILITY).ifPresent(provider -> heldItem.getCapability(CapabilityTemplate.TEMPLATE_KEY_CAPABILITY).ifPresent(key -> {\n            // Finally get the data from the render.\n            GadgetCopyPaste.getActivePos(player, heldItem).ifPresent(startPos -> {\n                MockDelegationWorld fakeWorld = new MockDelegationWorld(world);\n\n                BuildContext context = BuildContext.builder().player(player).stack(heldItem).build(fakeWorld);\n\n                // Get the template and move it to the start pos (player.pick())\n                IBuildView view = provider.getTemplateForKey(key).createViewInContext(context);\n\n                // Sort the render\n                List<PlacementTarget> targets = new ArrayList<>(view.estimateSize());\n                for (PlacementTarget target : view) {\n                    if (target.placeIn(context)) {\n                        targets.add(target);\n                    }\n                }\n                UUID id = provider.getId(key);\n                if (! id.equals(lastRendered))\n                    renderBuffer = null;\n                renderTargets(matrices, cameraView, context, targets, startPos, view);\n                lastRendered = id;\n            });\n        }));\n    }\n\n    private void renderTargets(PoseStack matrix, Vec3 projectedView, BuildContext context, List<PlacementTarget> targets, BlockPos startPos, IBuildView view) {\n        MultiBufferSource.BufferSource buffer = Minecraft.getInstance().renderBuffers().bufferSource();\n        VertexConsumer builder = buffer.getBuffer(OurRenderTypes.lines());\n\n        matrix.pushPose();\n        Matrix4f matrix4f = matrix.last().pose();\n        Matrix3f matrix3f = matrix.last().normal();\n\n        Region bb = view.getBoundingBox().translate(startPos.getX(), startPos.getY(), startPos.getZ());\n        float x = bb.getMinX(), y = bb.getMinY(), z = bb.getMinZ(),\n                dx = bb.getMaxX() + 1, dy = bb.getMaxY() + 1, dz = bb.getMaxZ() + 1;\n\n        builder.vertex(matrix4f, x, y, z).color(1F, 1F, 1F, 1F).normal(matrix3f, 1.0F, 0.0F, 0.0F).endVertex();\n        builder.vertex(matrix4f, dx, y, z).color(1F, 1F, 1F, 1F).normal(matrix3f, 1.0F, 0.0F, 0.0F).endVertex();\n        builder.vertex(matrix4f, x, y, z).color(1F, 1F, 1F, 1F).normal(matrix3f, 0.0F, 1.0F, 0.0F).endVertex();\n        builder.vertex(matrix4f, x, dy, z).color(1F, 1F, 1F, 1F).normal(matrix3f, 0.0F, 1.0F, 0.0F).endVertex();\n        builder.vertex(matrix4f, x, y, z).color(1F, 1F, 1F, 1F).normal(matrix3f, 0.0F, 0.0F, 1.0F).endVertex();\n        builder.vertex(matrix4f, x, y, dz).color(1F, 1F, 1F, 1F).normal(matrix3f, 0.0F, 0.0F, 1.0F).endVertex();\n        builder.vertex(matrix4f, dx, y, z).color(1F, 1F, 1F, 1F).normal(matrix3f, 0.0F, 1.0F, 0.0F).endVertex();\n        builder.vertex(matrix4f, dx, dy, z).color(1F, 1F, 1F, 1F).normal(matrix3f, 0.0F, 1.0F, 0.0F).endVertex();\n        builder.vertex(matrix4f, dx, dy, z).color(1F, 1F, 1F, 1F).normal(matrix3f, -1.0F, 0.0F, 0.0F).endVertex();\n        builder.vertex(matrix4f, x, dy, z).color(1F, 1F, 1F, 1F).normal(matrix3f, -1.0F, 0.0F, 0.0F).endVertex();\n        builder.vertex(matrix4f, x, dy, z).color(1F, 1F, 1F, 1F).normal(matrix3f, 0.0F, 0.0F, 1.0F).endVertex();\n        builder.vertex(matrix4f, x, dy, dz).color(1F, 1F, 1F, 1F).normal(matrix3f, 0.0F, 0.0F, 1.0F).endVertex();\n        builder.vertex(matrix4f, x, dy, dz).color(1F, 1F, 1F, 1F).normal(matrix3f, 0.0F, -1.0F, 0.0F).endVertex();\n        builder.vertex(matrix4f, x, y, dz).color(1F, 1F, 1F, 1F).normal(matrix3f, 0.0F, -1.0F, 0.0F).endVertex();\n        builder.vertex(matrix4f, x, y, dz).color(1F, 1F, 1F, 1F).normal(matrix3f, 1.0F, 0.0F, 0.0F).endVertex();\n        builder.vertex(matrix4f, dx, y, dz).color(1F, 1F, 1F, 1F).normal(matrix3f, 1.0F, 0.0F, 0.0F).endVertex();\n        builder.vertex(matrix4f, dx, y, dz).color(1F, 1F, 1F, 1F).normal(matrix3f, 0.0F, 0.0F, -1.0F).endVertex();\n        builder.vertex(matrix4f, dx, y, z).color(1F, 1F, 1F, 1F).normal(matrix3f, 0.0F, 0.0F, -1.0F).endVertex();\n        builder.vertex(matrix4f, x, dy, dz).color(1F, 1F, 1F, 1F).normal(matrix3f, 1.0F, 0.0F, 0.0F).endVertex();\n        builder.vertex(matrix4f, dx, dy, dz).color(1F, 1F, 1F, 1F).normal(matrix3f, 1.0F, 0.0F, 0.0F).endVertex();\n        builder.vertex(matrix4f, dx, y, dz).color(1F, 1F, 1F, 1F).normal(matrix3f, 0.0F, 1.0F, 0.0F).endVertex();\n        builder.vertex(matrix4f, dx, dy, dz).color(1F, 1F, 1F, 1F).normal(matrix3f, 0.0F, 1.0F, 0.0F).endVertex();\n        builder.vertex(matrix4f, dx, dy, z).color(1F, 1F, 1F, 1F).normal(matrix3f, 0.0F, 0.0F, 1.0F).endVertex();\n        builder.vertex(matrix4f, dx, dy, dz).color(1F, 1F, 1F, 1F).normal(matrix3f, 0.0F, 0.0F, 1.0F).endVertex();\n\n        buffer.endBatch(); // @mcp: draw = finish\n        matrix.popPose();\n\n        // TODO: fix me plz\n//        tickTrack++;\n//        if (renderBuffer != null && tickTrack < 300) {\n//            if (tickTrack % 30 == 0) {\n//                try {\n//                    Vec3 projectedView2 = projectedView;\n//                    Vec3 startPosView = new Vec3(startPos.getX(), startPos.getY(), startPos.getZ());\n//                    projectedView2 = projectedView2.subtract(startPosView);\n//                    renderBuffer.sort((float) projectedView2.x(), (float) projectedView2.y(), (float) projectedView2.z());\n//                } catch (Exception ignored) {\n//                }\n//            }\n//\n//            matrix.translate(startPos.getX(), startPos.getY(), startPos.getZ());\n//            renderBuffer.render(matrix.last().pose()); //Actually draw whats in the buffer\n//            return;\n//        }\n//\n////        List<BlockPos> blockPosList = sorter.getSortedTargets().stream().map(PlacementTarget::getPos).collect(Collectors.toList());\n//\n//        tickTrack = 0;\n//        if (renderBuffer != null) //Reset Render Buffer before rebuilding\n//            renderBuffer.close();\n//\n//        renderBuffer = MultiVBORenderer.of((buffer) -> {\n//            VertexConsumer builder = buffer.getBuffer(OurRenderTypes.RenderBlock);\n//            VertexConsumer noDepthbuilder = buffer.getBuffer(OurRenderTypes.CopyPasteRenderBlock);\n//\n//            BlockRenderDispatcher dispatcher = getMc().getBlockRenderer();\n//\n//            PoseStack stack = new PoseStack(); //Create a new matrix stack for use in the buffer building process\n//            stack.pushPose(); //Save position\n//\n//            for (PlacementTarget target : targets) {\n//                BlockPos targetPos = target.getPos();\n//                BlockState state = context.getWorld().getBlockState(target.getPos());\n//\n//                stack.pushPose(); //Save position again\n//                //matrix.translate(-startPos.getX(), -startPos.getY(), -startPos.getZ());\n//                stack.translate(targetPos.getX(), targetPos.getY(), targetPos.getZ());\n//\n//                BakedModel ibakedmodel = dispatcher.getBlockModel(state);\n//                BlockColors blockColors = Minecraft.getInstance().getBlockColors();\n//                int color = blockColors.getColor(state, context.getWorld(), targetPos, 0);\n//\n//                float f = (float) (color >> 16 & 255) / 255.0F;\n//                float f1 = (float) (color >> 8 & 255) / 255.0F;\n//                float f2 = (float) (color & 255) / 255.0F;\n//                try {\n//                    if (state.getRenderShape() == RenderShape.MODEL) {\n//                        for (Direction direction : Direction.values()) {\n//                            // TODO: likely broken this\n//                            if (Block.shouldRenderFace(state, context.getWorld(), targetPos, direction, target.getPos()) && !(context.getWorld().getBlockState(targetPos.relative(direction)).getBlock().equals(state.getBlock()))) {\n//                                if (state.getMaterial().isSolidBlocking()) {\n//                                    renderModelBrightnessColorQuads(stack.last(), builder, f, f1, f2, 0.7f, ibakedmodel.getQuads(state, direction, new Random(Mth.getSeed(targetPos)), EmptyModelData.INSTANCE), 15728640, 655360);\n//                                } else {\n//                                    renderModelBrightnessColorQuads(stack.last(), noDepthbuilder, f, f1, f2, 0.7f, ibakedmodel.getQuads(state, direction, new Random(Mth.getSeed(targetPos)), EmptyModelData.INSTANCE), 15728640, 655360);\n//                                }\n//                            }\n//                        }\n//                        if (state.getMaterial().isSolidBlocking())\n//                            renderModelBrightnessColorQuads(stack.last(), builder, f, f1, f2, 0.7f, ibakedmodel.getQuads(state, null, new Random(Mth.getSeed(targetPos)), EmptyModelData.INSTANCE), 15728640, 655360);\n//                        else\n//                            renderModelBrightnessColorQuads(stack.last(), noDepthbuilder, f, f1, f2, 0.7f, ibakedmodel.getQuads(state, null, new Random(Mth.getSeed(targetPos)), EmptyModelData.INSTANCE), 15728640, 655360);\n//                    }\n//                } catch (Exception e) {\n//                    BuildingGadgets.LOG.trace(\"Caught exception whilst rendering {}.\", state, e);\n//                }\n//\n//                stack.popPose(); // Load the position we saved earlier\n//            }\n//            stack.popPose(); //Load after loop\n//        });\n////        try {\n//            Vec3 projectedView2 = getMc().gameRenderer.getMainCamera().getPosition();\n//            Vec3 startPosView = new Vec3(startPos.getX(), startPos.getY(), startPos.getZ());\n//            projectedView2 = projectedView2.subtract(startPosView);\n//            renderBuffer.sort((float) projectedView2.x(), (float) projectedView2.y(), (float) projectedView2.z());\n////        } catch (Exception ignored) {\n////        }\n//        matrix.translate(startPos.getX(), startPos.getY(), startPos.getZ());\n//        renderBuffer.render(matrix.last().pose()); //Actually draw whats in the buffer\n    }\n\n    @Override\n    public boolean isLinkable() {\n        return true;\n    }\n\n    /**\n     * Vertex Buffer Object for caching the render. Pretty similar to how the chunk caching works\n     */\n    public static class MultiVBORenderer implements Closeable {\n        private static final int BUFFER_SIZE = 2 * 1024 * 1024 * 3;\n\n        public static MultiVBORenderer of(Consumer<MultiBufferSource> vertexProducer) {\n            final Map<RenderType, DireBufferBuilder> builders = Maps.newHashMap();\n\n            vertexProducer.accept(rt -> builders.computeIfAbsent(rt, (_rt) -> {\n                DireBufferBuilder builder = new DireBufferBuilder(BUFFER_SIZE);\n                builder.begin(_rt.mode().asGLMode, _rt.format());\n\n                return builder;\n            }));\n\n            Map<RenderType, DireBufferBuilder.State> sortCaches = Maps.newHashMap();\n            Map<RenderType, DireVertexBuffer> buffers = Maps.transformEntries(builders, (rt, builder) -> {\n                Objects.requireNonNull(rt);\n                Objects.requireNonNull(builder);\n                sortCaches.put(rt, builder.getVertexState());\n\n                builder.finishDrawing();\n                VertexFormat fmt = rt.format();\n                DireVertexBuffer vbo = new DireVertexBuffer(fmt);\n\n                vbo.upload(builder);\n                return vbo;\n            });\n\n            return new MultiVBORenderer(buffers, sortCaches);\n        }\n\n        private final ImmutableMap<RenderType, DireVertexBuffer> buffers;\n        private final ImmutableMap<RenderType, DireBufferBuilder.State> sortCaches;\n\n        protected MultiVBORenderer(Map<RenderType, DireVertexBuffer> buffers, Map<RenderType, DireBufferBuilder.State> sortCaches) {\n            this.buffers = ImmutableMap.copyOf(buffers);\n            this.sortCaches = ImmutableMap.copyOf(sortCaches);\n        }\n\n        public void sort(float x, float y, float z) {\n            for (Map.Entry<RenderType, DireBufferBuilder.State> kv : sortCaches.entrySet()) {\n                RenderType rt = kv.getKey();\n                DireBufferBuilder.State state = kv.getValue();\n                DireBufferBuilder builder = new DireBufferBuilder(BUFFER_SIZE);\n                builder.begin(rt.mode().asGLMode, rt.format());\n                builder.setVertexState(state);\n                builder.sortVertexData(x, y, z);\n                builder.finishDrawing();\n\n                DireVertexBuffer vbo = buffers.get(rt);\n                vbo.upload(builder);\n            }\n        }\n\n        public void render(Matrix4f matrix) {\n            buffers.forEach((rt, vbo) -> {\n                VertexFormat fmt = rt.format();\n\n                rt.setupRenderState();\n                vbo.bindBuffer();\n                fmt.setupBufferState();\n                vbo.draw(matrix, rt.mode().asGLMode);\n                DireVertexBuffer.unbindBuffer();\n                fmt.clearBufferState();\n                rt.clearRenderState();\n            });\n        }\n\n        public void close() {\n            buffers.values().forEach(DireVertexBuffer::close);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/client/renders/DestructionRender.java",
    "content": "package com.direwolf20.buildinggadgets.client.renders;\n\nimport com.direwolf20.buildinggadgets.client.renderer.OurRenderTypes;\nimport com.direwolf20.buildinggadgets.common.blocks.OurBlocks;\nimport com.direwolf20.buildinggadgets.common.items.AbstractGadget;\nimport com.direwolf20.buildinggadgets.common.items.GadgetDestruction;\nimport com.direwolf20.buildinggadgets.common.util.helpers.VectorHelper;\nimport com.mojang.blaze3d.systems.RenderSystem;\nimport com.mojang.blaze3d.vertex.PoseStack;\nimport com.mojang.blaze3d.vertex.VertexConsumer;\nimport net.minecraft.client.Minecraft;\nimport net.minecraft.client.renderer.MultiBufferSource;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.phys.BlockHitResult;\nimport net.minecraft.world.phys.Vec3;\nimport net.minecraftforge.client.event.RenderLevelStageEvent;\n\npublic class DestructionRender extends BaseRenderer {\n\n    @Override\n    public void render(RenderLevelStageEvent evt, Player player, ItemStack heldItem) {\n        if (!GadgetDestruction.getOverlay(heldItem))\n            return;\n\n        BlockHitResult lookingAt = VectorHelper.getLookingAt(player, heldItem);\n        Level world = player.level;\n        BlockPos anchor = ((AbstractGadget) heldItem.getItem()).getAnchor(heldItem);\n\n        if (world.getBlockState(VectorHelper.getLookingAt(player, heldItem).getBlockPos()) == AIR && anchor == null)\n            return;\n\n        BlockPos startBlock = (anchor == null) ? lookingAt.getBlockPos() : anchor;\n        Direction facing = (GadgetDestruction.getAnchorSide(heldItem) == null) ? lookingAt.getDirection() : GadgetDestruction.getAnchorSide(heldItem);\n        if (world.getBlockState(startBlock) == OurBlocks.EFFECT_BLOCK.get().defaultBlockState())\n            return;\n\n        Vec3 playerPos = getMc().gameRenderer.getMainCamera().getPosition();\n\n        PoseStack stack = evt.getPoseStack();\n        stack.pushPose();\n        stack.translate(-playerPos.x(), -playerPos.y(), -playerPos.z());\n\n        MultiBufferSource.BufferSource buffer = Minecraft.getInstance().renderBuffers().bufferSource();\n        VertexConsumer builder = buffer.getBuffer(OurRenderTypes.MissingBlockOverlay);\n\n        GadgetDestruction.getArea(world, startBlock, facing, player, heldItem)\n                .forEach(pos -> renderMissingBlock(stack.last().pose(), builder, pos));\n\n        stack.popPose();\n        RenderSystem.disableDepthTest();\n        buffer.endBatch(); // @mcp: draw = finish\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/client/screen/CopyGUI.java",
    "content": "package com.direwolf20.buildinggadgets.client.screen;\n\nimport com.direwolf20.buildinggadgets.client.screen.widgets.GuiIncrementer;\nimport com.direwolf20.buildinggadgets.common.config.Config;\nimport com.direwolf20.buildinggadgets.common.items.GadgetCopyPaste;\nimport com.direwolf20.buildinggadgets.common.network.PacketHandler;\nimport com.direwolf20.buildinggadgets.common.network.packets.PacketCopyCoords;\nimport com.direwolf20.buildinggadgets.common.tainted.building.Region;\nimport com.direwolf20.buildinggadgets.common.util.lang.GuiTranslation;\nimport com.mojang.blaze3d.vertex.PoseStack;\nimport net.minecraft.client.Minecraft;\nimport net.minecraft.client.gui.components.AbstractButton;\nimport net.minecraft.client.gui.components.Button;\nimport net.minecraft.client.gui.screens.Screen;\nimport net.minecraft.client.resources.language.I18n;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.world.item.ItemStack;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class CopyGUI extends Screen {\n    private GuiIncrementer startX, startY, startZ, endX, endY, endZ;\n\n    private boolean absoluteCoords = Config.GENERAL.absoluteCoordDefault.get() && Config.GENERAL.allowAbsoluteCoords.get();\n\n    private int x;\n    private int y;\n\n    private ItemStack copyPasteTool;\n    private BlockPos startPos;\n    private BlockPos endPos;\n\n    private List<GuiIncrementer> fields = new ArrayList<>();\n\n    public CopyGUI(ItemStack tool) {\n        super(Component.literal(\"\"));\n        this.copyPasteTool = tool;\n    }\n\n    @Override\n    public void init() {\n        super.init();\n\n        this.fields.clear();\n\n        this.x = width / 2;\n        this.y = height / 2;\n\n        Region reg = GadgetCopyPaste.getSelectedRegion(copyPasteTool).orElse(Region.singleZero());\n        startPos = reg.getMin();\n        endPos = reg.getMax();\n\n        int incrementerWidth = GuiIncrementer.WIDTH + (GuiIncrementer.WIDTH / 2);\n\n        fields.add(startX = new GuiIncrementer(x - incrementerWidth - 35, y - 40));\n        fields.add(startY = new GuiIncrementer(x - GuiIncrementer.WIDTH / 2, y - 40));\n        fields.add(startZ = new GuiIncrementer(x + (GuiIncrementer.WIDTH / 2) + 35, y - 40));\n        fields.add(endX = new GuiIncrementer(x - incrementerWidth - 35, y - 15));\n        fields.add(endY = new GuiIncrementer(x - GuiIncrementer.WIDTH / 2, y - 15));\n        fields.add(endZ = new GuiIncrementer(x + (GuiIncrementer.WIDTH / 2) + 35, y - 15));\n        fields.forEach(this::addRenderableWidget);\n\n        updateTextFields();\n\n        List<AbstractButton> buttons = new ArrayList<AbstractButton>() {{\n            add(new CenteredButton(y + 20, 50, GuiTranslation.SINGLE_CONFIRM.componentTranslation(), (button) -> {\n                if (absoluteCoords) {\n                    startPos = new BlockPos(startX.getValue(), startY.getValue(), startZ.getValue());\n                    endPos = new BlockPos(endX.getValue(), endY.getValue(), endZ.getValue());\n                } else {\n                    startPos = new BlockPos(startPos.getX() + startX.getValue(), startPos.getY() + startY.getValue(), startPos.getZ() + startZ.getValue());\n                    endPos = new BlockPos(startPos.getX() + endX.getValue(), startPos.getY() + endY.getValue(), startPos.getZ() + endZ.getValue());\n                }\n                PacketHandler.sendToServer(new PacketCopyCoords(startPos, endPos));\n            }));\n            add(new CenteredButton(y + 20, 50, GuiTranslation.SINGLE_CLOSE.componentTranslation(), (button) -> onClose()));\n            add(new CenteredButton(y + 20, 50, GuiTranslation.SINGLE_CLEAR.componentTranslation(), (button) -> {\n                PacketHandler.sendToServer(new PacketCopyCoords(BlockPos.ZERO, BlockPos.ZERO));\n                onClose();\n            }));\n\n            if (Config.GENERAL.allowAbsoluteCoords.get()) {\n                add(new CenteredButton(y + 20, 120, GuiTranslation.COPY_BUTTON_ABSOLUTE.componentTranslation(), (button) -> {\n                    coordsModeSwitch();\n                    updateTextFields();\n                }));\n            }\n        }};\n\n        CenteredButton.centerButtonList(buttons, x);\n        buttons.forEach(this::addRenderableWidget);\n    }\n\n    private void drawFieldLabel(PoseStack matrices, String name, int x, int y) {\n        font.drawShadow(matrices, name, this.x + x, this.y + y, 0xFFFFFF);\n    }\n\n    private void coordsModeSwitch() {\n        absoluteCoords = !absoluteCoords;\n    }\n\n    private void updateTextFields() {\n        if (absoluteCoords) {\n            BlockPos start = startX.getValue() != 0 ? new BlockPos(startPos.getX() + startX.getValue(), startPos.getY() + startY.getValue(), startPos.getZ() + startZ.getValue()) : startPos;\n            BlockPos end = endX.getValue() != 0 ? new BlockPos(startPos.getX() + endX.getValue(), startPos.getY() + endY.getValue(), startPos.getZ() + endZ.getValue()) : endPos;\n\n            startX.setValue(start.getX());\n            startY.setValue(start.getY());\n            startZ.setValue(start.getZ());\n            endX.setValue(end.getX());\n            endY.setValue(end.getY());\n            endZ.setValue(end.getZ());\n        } else {\n            startX.setValue(startX.getValue() != 0 ? startX.getValue() - startPos.getX() : 0);\n            startY.setValue(startY.getValue() != 0 ? startY.getValue() - startPos.getY() : 0);\n            startZ.setValue(startZ.getValue() != 0 ? startZ.getValue() - startPos.getZ() : 0);\n            endX.setValue(endX.getValue() != 0 ? endX.getValue() - startPos.getX() : endPos.getX() - startPos.getX());\n            endY.setValue(endY.getValue() != 0 ? endY.getValue() - startPos.getY() : endPos.getY() - startPos.getY());\n            endZ.setValue(endZ.getValue() != 0 ? endZ.getValue() - startPos.getZ() : endPos.getZ() - startPos.getZ());\n        }\n    }\n\n    @Override\n    public void render(PoseStack matrices, int mouseX, int mouseY, float partialTicks) {\n        drawFieldLabel(matrices, GuiTranslation.FIELD_START.format() + \" X\", -175, -36);\n        drawFieldLabel(matrices, \"Y\", -45, -36);\n        drawFieldLabel(matrices, \"Z\", 55, -36);\n        drawFieldLabel(matrices, GuiTranslation.FIELD_END.format() + \" X\", 8 - 175, -11);\n        drawFieldLabel(matrices, \"Y\", -45, -11);\n        drawFieldLabel(matrices, \"Z\", 55, -11);\n\n        drawCenteredString(matrices, Minecraft.getInstance().font, I18n.get(GuiTranslation.COPY_LABEL_HEADING.getTranslationKey()), this.x, this.y - 80, 0xFFFFFF);\n        drawCenteredString(matrices, Minecraft.getInstance().font, I18n.get(GuiTranslation.COPY_LABEL_SUBHEADING.getTranslationKey()), this.x, this.y - 68, 0xFFFFFF);\n\n        super.render(matrices, mouseX, mouseY, partialTicks);\n    }\n\n    @Override\n    public boolean keyPressed(int mouseX, int mouseY, int __unused) {\n        fields.forEach(button -> button.keyPressed(mouseX, mouseY, __unused));\n        return super.keyPressed(mouseX, mouseY, __unused);\n    }\n\n    @Override\n    public boolean charTyped(char charTyped, int __unused) {\n        fields.forEach(button -> button.charTyped(charTyped, __unused));\n        return false;\n    }\n\n    @Override\n    public boolean isPauseScreen() {\n        return false;\n    }\n\n    static class CenteredButton extends Button {\n        CenteredButton(int y, int width, Component text, OnPress onPress) {\n            super(builder(text, onPress)\n                    .pos(0, y)\n                    .size(width, 20)\n            );\n        }\n\n        static void centerButtonList(List<AbstractButton> buttons, int startX) {\n            int collectiveWidth = buttons.stream().mapToInt(AbstractButton::getWidth).sum() + (buttons.size() - 1) * 5;\n\n            int nextX = startX - collectiveWidth / 2;\n            for (AbstractButton button : buttons) {\n                button.setX(nextX);\n                nextX += button.getWidth() + 5;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/client/screen/DestructionGUI.java",
    "content": "package com.direwolf20.buildinggadgets.client.screen;\n\nimport com.direwolf20.buildinggadgets.client.screen.widgets.IncrementalSliderWidget;\nimport com.direwolf20.buildinggadgets.common.config.Config;\nimport com.direwolf20.buildinggadgets.common.items.GadgetDestruction;\nimport com.direwolf20.buildinggadgets.common.network.PacketHandler;\nimport com.direwolf20.buildinggadgets.common.network.packets.PacketDestructionGUI;\nimport com.direwolf20.buildinggadgets.common.util.lang.GuiTranslation;\nimport com.direwolf20.buildinggadgets.common.util.lang.MessageTranslation;\nimport com.mojang.blaze3d.vertex.PoseStack;\nimport net.minecraft.client.Minecraft;\nimport net.minecraft.client.gui.components.Button;\nimport net.minecraft.client.gui.screens.Screen;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.world.item.ItemStack;\n\nimport javax.annotation.Nonnull;\nimport java.util.HashSet;\nimport java.util.Set;\n\npublic class DestructionGUI extends Screen {\n    private final Set<IncrementalSliderWidget> sliders = new HashSet<>();\n\n    private IncrementalSliderWidget left;\n    private IncrementalSliderWidget right;\n    private IncrementalSliderWidget up;\n    private IncrementalSliderWidget down;\n    private IncrementalSliderWidget depth;\n\n    private Button confirm;\n\n    private String sizeString = \"\";\n    private boolean isValidSize = true;\n\n    private final ItemStack destructionGadget;\n\n    public DestructionGUI(ItemStack tool) {\n        super(Component.empty());\n        this.destructionGadget = tool;\n    }\n\n    @Override\n    public void init() {\n        super.init();\n\n        int x = width / 2;\n        int y = height / 2;\n\n        this.addRenderableWidget(confirm = Button.builder(Component.translatable(GuiMod.getLangKeySingle(\"confirm\")), b -> {\n            if (Minecraft.getInstance().player == null) {\n                return;\n            }\n\n            if (isWithinBounds()) {\n                PacketHandler.sendToServer(new PacketDestructionGUI(left.getValueInt(), right.getValueInt(), up.getValueInt(), down.getValueInt(), depth.getValueInt()));\n                this.onClose();\n            } else\n                Minecraft.getInstance().player.displayClientMessage(MessageTranslation.DESTRCUT_TOO_LARGE.componentTranslation(Config.GADGETS.GADGET_DESTRUCTION.destroySize.get()), true);\n        })\n                .pos((x - 30) + 32, y + 65)\n                .size(60, 20)\n                .build());\n\n        this.addRenderableWidget(Button.builder(Component.translatable(GuiMod.getLangKeySingle(\"cancel\")), b -> onClose())\n                .pos((x - 30) - 32, y + 65)\n                .size(60, 20)\n                .build());\n\n        sliders.clear();\n        sliders.add(depth = this.createSlider(x - (70 / 2), y - (14 / 2), GuiTranslation.SINGLE_DEPTH, GadgetDestruction.getToolValue(destructionGadget, \"depth\")));\n        sliders.add(right = this.createSlider(x + (70 + 5), y - (14 / 2), GuiTranslation.SINGLE_RIGHT, GadgetDestruction.getToolValue(destructionGadget, \"right\")));\n        sliders.add(left = this.createSlider(x - (70 * 2) - 5, y - (14 / 2), GuiTranslation.SINGLE_LEFT, GadgetDestruction.getToolValue(destructionGadget, \"left\")));\n        sliders.add(up = this.createSlider(x - (70 / 2), y - 35, GuiTranslation.SINGLE_UP, GadgetDestruction.getToolValue(destructionGadget, \"up\")));\n        sliders.add(down = this.createSlider(x - (70 / 2), y + 20, GuiTranslation.SINGLE_DOWN, GadgetDestruction.getToolValue(destructionGadget, \"down\")));\n\n        updateSizeString();\n        updateIsValid();\n\n        // Adds their buttons to the gui\n        sliders.forEach(gui -> gui.getComponents().forEach(this::addRenderableWidget));\n    }\n\n    public IncrementalSliderWidget createSlider(int x, int y, GuiTranslation prefix, int value) {\n        return new IncrementalSliderWidget(x, y, 70, 14, 0D, 16D, prefix.componentTranslation().append(\": \"), value, this::onSliderUpdate);\n    }\n\n    public void onSliderUpdate(IncrementalSliderWidget widget) {\n        this.updateSizeString();\n        this.updateIsValid();\n    }\n\n    private boolean isWithinBounds() {\n        int x = 1 + left.getValueInt() + right.getValueInt();\n        int y = 1 + up.getValueInt() + down.getValueInt();\n        int z = depth.getValueInt();\n        int dim = Config.GADGETS.GADGET_DESTRUCTION.destroySize.get();\n\n        return x <= (dim + 1) && y <= (dim + 1) && z <= dim;\n    }\n\n    private String getSizeString() {\n        int x = 1 + left.getValueInt() + right.getValueInt();\n        int y = 1 + up.getValueInt() + down.getValueInt();\n        int z = depth.getValueInt();\n\n        return String.format(\"%d x %d x %d\", x, y, z);\n    }\n\n    private void updateIsValid() {\n        this.isValidSize = isWithinBounds();\n        if (!isValidSize && this.confirm.active) {\n            this.confirm.setFGColor(0xFF2000);\n            this.confirm.active = false;\n        }\n\n        if (isValidSize && !this.confirm.active) {\n            this.confirm.clearFGColor();\n            this.confirm.active = true;\n        }\n    }\n\n    private void updateSizeString() {\n        this.sizeString = getSizeString();\n    }\n\n    @Override\n    public void render(@Nonnull PoseStack matrices, int mouseX, int mouseY, float partialTicks) {\n        super.render(matrices, mouseX, mouseY, partialTicks);\n\n        drawCenteredString(matrices, font, this.sizeString, width / 2, (height / 2) + 40, this.isValidSize ? 0x00FF00 : 0xFF2000);\n        if (!this.isValidSize) {\n            drawCenteredString(matrices, font, MessageTranslation.DESTRCUT_TOO_LARGE.format(Config.GADGETS.GADGET_DESTRUCTION.destroySize.get()), width / 2, (height / 2) + 50, 0xFF2000);\n        }\n    }\n\n    @Override\n    public boolean mouseReleased(double mouseX, double mouseY, int mouseButton) {\n        return super.mouseReleased(mouseX, mouseY, mouseButton);\n    }\n\n    @Override\n    public boolean isPauseScreen() {\n        return false;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/client/screen/GuiMod.java",
    "content": "package com.direwolf20.buildinggadgets.client.screen;\n\nimport com.direwolf20.buildinggadgets.common.items.GadgetCopyPaste;\nimport com.direwolf20.buildinggadgets.common.items.GadgetDestruction;\nimport com.direwolf20.buildinggadgets.common.items.TemplateItem;\nimport com.direwolf20.buildinggadgets.common.util.lang.LangUtil;\nimport net.minecraft.client.Minecraft;\nimport net.minecraft.client.gui.screens.Screen;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.item.ItemStack;\n\nimport java.util.function.Function;\nimport java.util.function.Supplier;\n\npublic enum GuiMod {\n    COPY(GadgetCopyPaste::getGadget, stack -> () -> new CopyGUI(stack)),\n    PASTE(GadgetCopyPaste::getGadget, stack -> () -> new PasteGUI(stack)),\n    DESTRUCTION(GadgetDestruction::getGadget, stack -> () -> new DestructionGUI(stack)),\n    MATERIAL_LIST(TemplateItem::getTemplateItem, stack -> () -> new MaterialListGUI(stack));\n\n    private final Function<Player, ItemStack> stackReader;\n    private final Function<ItemStack, Supplier<? extends Screen>> clientScreenProvider;\n\n    GuiMod(Function<Player, ItemStack> stackReader, Function<ItemStack, Supplier<? extends Screen>> clientScreenProvider) {\n        this.stackReader = stackReader;\n        this.clientScreenProvider = clientScreenProvider;\n    }\n\n    public boolean openScreen(Player player) {\n        if (clientScreenProvider == null)\n            return false;\n\n        ItemStack stack = stackReader.apply(player);\n        if (stack == null || stack.isEmpty())\n            return false;\n\n        Screen screen = clientScreenProvider.apply(stack).get();\n        Minecraft.getInstance().setScreen(screen);\n        return screen == null;\n    }\n\n    public static String getLangKeySingle(String name) {\n        return LangUtil.getLangKey(\"gui\", \"single\", name);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/client/screen/MaterialListGUI.java",
    "content": "package com.direwolf20.buildinggadgets.client.screen;\n\nimport com.direwolf20.buildinggadgets.common.BuildingGadgets;\nimport com.direwolf20.buildinggadgets.common.capability.CapabilityTemplate;\nimport com.direwolf20.buildinggadgets.common.tainted.building.view.BuildContext;\nimport com.direwolf20.buildinggadgets.common.tainted.template.ITemplateKey;\nimport com.direwolf20.buildinggadgets.common.tainted.template.ITemplateProvider;\nimport com.direwolf20.buildinggadgets.common.tainted.template.Template;\nimport com.direwolf20.buildinggadgets.common.tainted.template.TemplateHeader;\nimport com.direwolf20.buildinggadgets.common.util.lang.MaterialListTranslation;\nimport com.direwolf20.buildinggadgets.common.util.ref.Reference;\nimport com.google.common.base.Preconditions;\nimport com.google.common.collect.ImmutableList;\nimport com.google.common.collect.Lists;\nimport com.mojang.blaze3d.systems.RenderSystem;\nimport com.mojang.blaze3d.vertex.PoseStack;\nimport net.minecraft.client.Minecraft;\nimport net.minecraft.client.gui.Font;\nimport net.minecraft.client.gui.components.Button;\nimport net.minecraft.client.gui.components.events.GuiEventListener;\nimport net.minecraft.client.gui.screens.Screen;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.resources.ResourceLocation;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraftforge.common.util.LazyOptional;\n\nimport java.awt.*;\nimport java.util.List;\nimport java.util.UUID;\n\npublic class MaterialListGUI extends Screen implements ITemplateProvider.IUpdateListener {\n\n    public static final int BUTTON_HEIGHT = 20;\n    public static final int BUTTONS_PADDING = 4;\n\n    public static final ResourceLocation BACKGROUND_TEXTURE = new ResourceLocation(Reference.MODID, \"textures/gui/material_list.png\");\n    public static final int BACKGROUND_WIDTH = 256;\n    public static final int BACKGROUND_HEIGHT = 200;\n    public static final int BORDER_SIZE = 4;\n\n    public static final int WINDOW_WIDTH = BACKGROUND_WIDTH - BORDER_SIZE * 2;\n    public static final int WINDOW_HEIGHT = BACKGROUND_HEIGHT - BORDER_SIZE * 2;\n\n    private int backgroundX;\n    private int backgroundY;\n    private ItemStack item;\n\n    private String title;\n    private int titleLeft;\n    private int titleTop;\n\n    private ScrollingMaterialList scrollingList;\n\n    private Button buttonClose;\n    private Button buttonSortingModes;\n    private Button buttonCopyList;\n\n    private int hoveringTextX;\n    private int hoveringTextY;\n    private List<Component> hoveringText;\n    private TemplateHeader header;\n\n    public MaterialListGUI(ItemStack item) {\n        super(MaterialListTranslation.TITLE.componentTranslation());\n        Preconditions.checkArgument(item.getCapability(CapabilityTemplate.TEMPLATE_KEY_CAPABILITY).isPresent());\n        this.item = item;\n    }\n\n    @Override\n    public void init() {\n        this.backgroundX = getXForAlignedCenter(0, width, BACKGROUND_WIDTH);\n        this.backgroundY = getYForAlignedCenter(0, height, BACKGROUND_HEIGHT);\n\n        header = evaluateTemplateHeader();\n        evaluateTitle();\n\n        this.scrollingList = new ScrollingMaterialList(this);\n        // Make it receive mouse scroll events, so that the player can use his mouse wheel at the start\n        this.setFocused(scrollingList);\n        this.addRenderableWidget(scrollingList);\n\n        int buttonY = getWindowBottomY() - (ScrollingMaterialList.BOTTOM / 2 + BUTTON_HEIGHT / 2);\n        this.buttonClose = Button.builder(MaterialListTranslation.BUTTON_CLOSE.componentTranslation(), b -> getMinecraft().player.closeContainer())\n                .pos(0, buttonY)\n                .size( 0, BUTTON_HEIGHT)\n                .build();\n\n        this.buttonSortingModes = Button.builder(scrollingList.getSortingMode().getTranslationProvider().componentTranslation(), (button) -> {\n            scrollingList.setSortingMode(scrollingList.getSortingMode().next());\n            buttonSortingModes.setMessage(scrollingList.getSortingMode().getTranslationProvider().componentTranslation());\n        })\n                .pos(0, buttonY)\n                .size(0, BUTTON_HEIGHT)\n                .build();\n\n        this.buttonCopyList = Button.builder(MaterialListTranslation.BUTTON_COPY.componentTranslation(), (button) -> {\n            getMinecraft().keyboardHandler.setClipboard(evaluateTemplateHeader().toJson(false, hasControlDown()));\n\n            if (getMinecraft().player != null)\n                getMinecraft().player.displayClientMessage(Component.translatable(MaterialListTranslation.MESSAGE_COPY_SUCCESS.getTranslationKey()), true);\n        })\n                .pos(0, buttonY)\n                .size(0, BUTTON_HEIGHT)\n                .build();\n\n        // Buttons will be placed left to right in this order\n        this.addRenderableWidget(buttonSortingModes);\n        this.addRenderableWidget(buttonCopyList);\n        this.addRenderableWidget(buttonClose);\n\n        this.calculateButtonsWidthAndX();\n    }\n\n    public TemplateHeader evaluateTemplateHeader() {\n        Template template = getTemplateCapability();\n\n        BuildContext context = BuildContext.builder()\n                .player(getMinecraft().player)\n                .stack(getTemplateItem())\n                .build(getMinecraft().level);\n\n        return template.getHeaderAndForceMaterials(context);\n    }\n\n    public TemplateHeader getHeader() {\n        return header;\n    }\n\n    @Override\n    public void render(PoseStack matrices, int mouseX, int mouseY, float particleTicks) {\n        RenderSystem.setShaderTexture(0, BACKGROUND_TEXTURE);\n        blit(matrices, backgroundX, backgroundY, 0, 0, BACKGROUND_WIDTH, BACKGROUND_HEIGHT); // TODO: Might be wrong\n\n        scrollingList.render(matrices, mouseX, mouseY, particleTicks);\n        drawString(matrices, font, title, titleLeft, titleTop, Color.WHITE.getRGB());\n        super.render(matrices, mouseX, mouseY, particleTicks);\n\n        if (buttonCopyList.isMouseOver(mouseX, mouseY)) {\n            renderTooltip(matrices, Lists.transform(ImmutableList.of(MaterialListTranslation.HELP_COPY_LIST.componentTranslation()), Component::getVisualOrderText), mouseX, mouseY);\n//            GuiUtils.drawHoveringText(matrices, ImmutableList.of(MaterialListTranslation.HELP_COPY_LIST.componentTranslation()), mouseX, mouseY, width, height, Integer.MAX_VALUE, textRenderer);\n        } else if (hoveringText != null) {\n            renderTooltip(matrices, Lists.transform(hoveringText, Component::getVisualOrderText), mouseX, mouseY);\n\n//            GuiUtils.drawHoveringText(matrices, hoveringText, hoveringTextX, hoveringTextY, width, height, Integer.MAX_VALUE, textRenderer);\n            hoveringText = null;\n        }\n    }\n\n    private void calculateButtonsWidthAndX() {\n        // This part would can create narrower buttons when there are too few of them, due to the vanilla button texture is 200 pixels wide\n        int amountButtons = (int) children().stream().filter(e -> e instanceof Button).count();\n        int amountMargins = amountButtons - 1;\n        int totalMarginWidth = amountMargins * BUTTONS_PADDING;\n        int usableWidth = getWindowWidth();\n        int buttonWidth = (usableWidth - totalMarginWidth) / amountButtons;\n\n        // Align the box of buttons in the center, and start from the left\n        int nextX = getWindowLeftX();\n\n        for (GuiEventListener widget : children()) {\n            if (widget instanceof Button btn) {\n                btn.setWidth(buttonWidth);\n                btn.setX(nextX);\n                nextX += buttonWidth + BUTTONS_PADDING;\n            }\n        }\n    }\n\n    public Template getTemplateCapability() {\n        if (getMinecraft().level == null || getMinecraft().player == null)\n            return null;\n\n        LazyOptional<ITemplateProvider> providerCap = getMinecraft().level.getCapability(CapabilityTemplate.TEMPLATE_PROVIDER_CAPABILITY);\n        if (providerCap.isPresent()) {\n            LazyOptional<ITemplateKey> keyCap = item.getCapability(CapabilityTemplate.TEMPLATE_KEY_CAPABILITY);\n            ITemplateProvider provider = providerCap.orElseThrow(RuntimeException::new);\n            if (keyCap.isPresent()) {\n                provider.registerUpdateListener(this);\n                ITemplateKey key = keyCap.orElseThrow(RuntimeException::new);\n                return provider.getTemplateForKey(key);\n            }\n            BuildingGadgets.LOG.warn(\"Item used for material list does not have an ITemplateKey capability!\");\n            getMinecraft().player.closeContainer();\n            return null;\n        }\n\n        BuildingGadgets.LOG.warn(\"Client world used for material list does not have an ITemplateProvider capability!\");\n        getMinecraft().player.closeContainer();\n        return null;\n    }\n\n    public void setTaskHoveringText(int x, int y, List<Component> text) {\n        hoveringTextX = x;\n        hoveringTextY = y;\n        hoveringText = text;\n    }\n\n    @Override\n    public void onTemplateUpdate(ITemplateProvider provider, ITemplateKey key, Template template) {\n        item.getCapability(CapabilityTemplate.TEMPLATE_KEY_CAPABILITY).ifPresent(itemKey -> {\n            UUID keyId = provider.getId(key);\n            UUID itemId = provider.getId(itemKey);\n            if (keyId.equals(itemId)) {\n                header = evaluateTemplateHeader();\n                evaluateTitle();\n                scrollingList.reset();\n            }\n        });\n    }\n\n    private void evaluateTitle() {\n        String name = getHeader().getName();\n        String author = getHeader().getAuthor();\n\n        this.title = name == null && author == null ? MaterialListTranslation.TITLE_EMPTY.format()\n                : name == null ? MaterialListTranslation.TITLE_AUTHOR_ONLY.format(author)\n                : author == null ? MaterialListTranslation.TITLE_NAME_ONLY.format(name)\n                : MaterialListTranslation.TITLE.format(name, author);\n\n        this.titleTop = getYForAlignedCenter(backgroundY, getWindowTopY() + ScrollingMaterialList.TOP, font.lineHeight);\n        this.titleLeft = getXForAlignedCenter(backgroundX, getWindowRightX(), font.width(title));\n    }\n\n    @Override\n    public boolean isPauseScreen() {\n        return false;\n    }\n\n    public int getWindowLeftX() {\n        return backgroundX + BORDER_SIZE;\n    }\n\n    public int getWindowRightX() {\n        return backgroundX + BACKGROUND_WIDTH - BORDER_SIZE;\n    }\n\n    public int getWindowTopY() {\n        return backgroundY + BORDER_SIZE;\n    }\n\n    public int getWindowBottomY() {\n        return backgroundY + BACKGROUND_HEIGHT - BORDER_SIZE;\n    }\n\n    public int getWindowWidth() {\n        return WINDOW_WIDTH;\n    }\n\n    public int getWindowHeight() {\n        return WINDOW_HEIGHT;\n    }\n\n    public ItemStack getTemplateItem() {\n        return item;\n    }\n\n    public static int getXForAlignedRight(int right, int width) {\n        return right - width;\n    }\n\n    public static int getXForAlignedCenter(int left, int right, int width) {\n        return left + (right - left) / 2 - width / 2;\n    }\n\n    public static int getYForAlignedCenter(int top, int bottom, int height) {\n        return top + (bottom - top) / 2 - height / 2;\n    }\n\n    public static void renderTextVerticalCenter(PoseStack matrices, String text, int leftX, int top, int bottom, int color) {\n        Font fontRenderer = Minecraft.getInstance().font;\n        int y = getYForAlignedCenter(top, bottom, fontRenderer.lineHeight);\n        fontRenderer.draw(matrices, text, leftX, y, color);\n    }\n\n    public static void renderTextHorizontalRight(PoseStack matrices, String text, int right, int y, int color) {\n        Font fontRenderer = Minecraft.getInstance().font;\n        int x = getXForAlignedRight(right, fontRenderer.width(text));\n        fontRenderer.draw(matrices, text, x, y, color);\n    }\n\n    public static boolean isPointInBox(double x, double y, int bx, int by, int width, int height) {\n        return x >= bx &&\n                y >= by &&\n                x < bx + width &&\n                y < by + height;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/client/screen/ModeRadialMenu.java",
    "content": "/**\n * This class was adapted from code written by Vazkii for the PSI mod: https://github.com/Vazkii/Psi\n * Psi is Open Source and distributed under the\n * Psi License: http://psi.vazkii.us/license.php\n */\npackage com.direwolf20.buildinggadgets.client.screen;\n\nimport com.direwolf20.buildinggadgets.client.KeyBindings;\nimport com.direwolf20.buildinggadgets.client.OurSounds;\nimport com.direwolf20.buildinggadgets.client.renderer.OurRenderTypes;\nimport com.direwolf20.buildinggadgets.client.screen.widgets.GuiIconActionable;\nimport com.direwolf20.buildinggadgets.client.screen.widgets.IncrementalSliderWidget;\nimport com.direwolf20.buildinggadgets.common.config.Config;\nimport com.direwolf20.buildinggadgets.common.items.*;\nimport com.direwolf20.buildinggadgets.common.items.modes.BuildingModes;\nimport com.direwolf20.buildinggadgets.common.items.modes.ExchangingModes;\nimport com.direwolf20.buildinggadgets.common.network.PacketHandler;\nimport com.direwolf20.buildinggadgets.common.network.packets.*;\nimport com.direwolf20.buildinggadgets.common.util.GadgetUtils;\nimport com.direwolf20.buildinggadgets.common.util.lang.GuiTranslation;\nimport com.direwolf20.buildinggadgets.common.util.lang.MessageTranslation;\nimport com.direwolf20.buildinggadgets.common.util.lang.RadialTranslation;\nimport com.direwolf20.buildinggadgets.common.util.lang.Styles;\nimport com.direwolf20.buildinggadgets.common.util.ref.Reference;\nimport com.google.common.collect.ImmutableList;\nimport com.google.common.collect.ImmutableSet;\nimport com.mojang.blaze3d.platform.InputConstants;\nimport com.mojang.blaze3d.systems.RenderSystem;\nimport com.mojang.blaze3d.vertex.PoseStack;\nimport com.mojang.blaze3d.vertex.VertexConsumer;\nimport net.minecraft.client.KeyMapping;\nimport net.minecraft.client.Minecraft;\nimport net.minecraft.client.gui.components.Button;\nimport net.minecraft.client.gui.components.events.GuiEventListener;\nimport net.minecraft.client.gui.screens.Screen;\nimport net.minecraft.client.renderer.GameRenderer;\nimport net.minecraft.client.renderer.MultiBufferSource;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.resources.ResourceLocation;\nimport net.minecraft.world.item.Item;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.phys.Vec3;\nimport net.minecraftforge.common.ForgeI18n;\nimport org.joml.Matrix4f;\n\nimport java.awt.*;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.function.Predicate;\nimport java.util.stream.Collectors;\n\npublic class ModeRadialMenu extends Screen {\n    private static final ImmutableList<ResourceLocation> signsCopyPaste = ImmutableList.of(\n            new ResourceLocation(Reference.MODID, \"textures/gui/mode/copy.png\"),\n            new ResourceLocation(Reference.MODID, \"textures/gui/mode/paste.png\")\n    );\n    private final List<Button> conditionalButtons = new ArrayList<>();\n    private int timeIn = 0;\n    private int slotSelected = -1;\n    private int segments;\n\n    public ModeRadialMenu(ItemStack stack) {\n        super(Component.literal(\"\"));\n\n        if (stack.getItem() instanceof AbstractGadget) {\n            this.setSocketable(stack);\n        }\n    }\n\n    private static float mouseAngle(int x, int y, int mx, int my) {\n        Vector2f baseVec = new Vector2f(1F, 0F);\n        Vector2f mouseVec = new Vector2f(mx - x, my - y);\n\n        float ang = (float) (Math.acos(baseVec.dot(mouseVec) / (baseVec.length() * mouseVec.length())) * (180F / Math.PI));\n        return my < y\n                ? 360F - ang\n                : ang;\n    }\n\n    public void setSocketable(ItemStack stack) {\n        if (stack.getItem() instanceof GadgetBuilding) {\n            this.segments = BuildingModes.values().length;\n        } else if (stack.getItem() instanceof GadgetExchanger) {\n            this.segments = ExchangingModes.values().length;\n        } else if (stack.getItem() instanceof GadgetCopyPaste) {\n            this.segments = GadgetCopyPaste.ToolMode.values().length;\n        }\n    }\n\n    @Override\n    public void init() {\n        this.conditionalButtons.clear();\n        ItemStack tool = this.getGadget();\n        boolean isDestruction = tool.getItem() instanceof GadgetDestruction;\n        ScreenPosition right = isDestruction\n                ? ScreenPosition.TOP\n                : ScreenPosition.RIGHT;\n        ScreenPosition left = isDestruction\n                ? ScreenPosition.BOTTOM\n                : ScreenPosition.LEFT;\n\n        if (isDestruction) {\n            addRenderableWidget(new PositionedIconActionable(RadialTranslation.DESTRUCTION_OVERLAY, \"destroy_overlay\", right, send -> {\n                if (send)\n                    PacketHandler.sendToServer(new PacketChangeRange());\n\n                return GadgetDestruction.getOverlay(this.getGadget());\n            }));\n\n            addRenderableWidget(new PositionedIconActionable(RadialTranslation.FLUID_ONLY, \"fluid_only\", right, send -> {\n                if (send)\n                    PacketHandler.sendToServer(new PacketToggleFluidOnly());\n\n                return GadgetDestruction.getIsFluidOnly(this.getGadget());\n            }));\n        } else {\n            addRenderableWidget(new PositionedIconActionable(RadialTranslation.ROTATE, \"rotate\", left, false, send -> {\n                if (send)\n                    PacketHandler.sendToServer(new PacketRotateMirror(PacketRotateMirror.Operation.ROTATE));\n\n                return false;\n            }));\n            addRenderableWidget(new PositionedIconActionable(RadialTranslation.MIRROR, \"mirror\", left, false, send -> {\n                if (send)\n                    PacketHandler.sendToServer(new PacketRotateMirror(PacketRotateMirror.Operation.MIRROR));\n\n                return false;\n            }));\n        }\n        if (!(tool.getItem() instanceof GadgetCopyPaste)) {\n            if (!isDestruction || Config.GADGETS.GADGET_DESTRUCTION.nonFuzzyEnabled.get()) {\n                Button button = new PositionedIconActionable(RadialTranslation.FUZZY, \"fuzzy\", right, send -> {\n                    if (send) {\n                        PacketHandler.sendToServer(new PacketToggleFuzzy());\n                    }\n\n                    return AbstractGadget.getFuzzy(this.getGadget());\n                });\n                addRenderableWidget(button);\n                conditionalButtons.add(button);\n            }\n            if (!isDestruction) {\n                Button button = new PositionedIconActionable(RadialTranslation.CONNECTED_SURFACE, \"connected_area\", right, send -> {\n                    if (send) {\n                        PacketHandler.sendToServer(new PacketToggleConnectedArea());\n                    }\n\n                    return AbstractGadget.getConnectedArea(this.getGadget());\n                });\n                addRenderableWidget(button);\n                conditionalButtons.add(button);\n            }\n            if (!isDestruction) {\n                int widthSlider = 82;\n                IncrementalSliderWidget sliderRange = new IncrementalSliderWidget(width / 2 - widthSlider / 2, height / 2 + 72, widthSlider, 14, 1, Config.GADGETS.maxRange.get(), GuiTranslation.SINGLE_RANGE.componentTranslation().append(\": \"),  GadgetUtils.getToolRange(tool), slider -> {\n                    sendRangeUpdate(slider.getValueInt());\n                });\n//                        GadgetUtils.getToolRange(tool), false, true, Color.DARK_GRAY, slider -> {\n//                    GuiSliderInt sliderI = (GuiSliderInt) slider;\n//                    this.sendRangeUpdate(sliderI.getValueInt());\n//                }, (slider, amount) -> {\n//                    int value = slider.getValueInt();\n//                    int valueNew = Mth.clamp(value + amount, 1, Config.GADGETS.maxRange.get());\n//                    sendRangeUpdate(valueNew);\n//                    slider.setValue(valueNew);\n//                    slider.updateSlider();\n//                }\n//                );\n                sliderRange.getComponents().forEach(this::addRenderableWidget);\n            }\n        } else {\n            // Copy Paste specific\n            addRenderableWidget(new PositionedIconActionable(RadialTranslation.OPEN_GUI, \"copypaste_opengui\", right, send -> {\n                if (!send)\n                    return false;\n\n                assert this.getMinecraft().player != null;\n\n                getMinecraft().player.closeContainer();\n                if (GadgetCopyPaste.getToolMode(tool) == GadgetCopyPaste.ToolMode.COPY)\n                    getMinecraft().setScreen(new CopyGUI(tool));\n                else\n                    getMinecraft().setScreen(new PasteGUI(tool));\n                return true;\n            }));\n            addRenderableWidget(new PositionedIconActionable(RadialTranslation.OPEN_MATERIAL_LIST, \"copypaste_materiallist\", right, send -> {\n                if (!send)\n                    return false;\n\n                assert this.getMinecraft().player != null;\n\n                getMinecraft().player.closeContainer();\n                getMinecraft().setScreen(new MaterialListGUI(tool));\n                return true;\n            }));\n        }\n        addRenderableWidget(new PositionedIconActionable(RadialTranslation.RAYTRACE_FLUID, \"raytrace_fluid\", right, send -> {\n            if (send)\n                PacketHandler.sendToServer(new PacketToggleRayTraceFluid());\n\n            return AbstractGadget.shouldRayTraceFluid(this.getGadget());\n        }));\n        if (tool.getItem() instanceof GadgetBuilding) {\n            addRenderableWidget(new PositionedIconActionable(RadialTranslation.PLACE_ON_TOP, \"building_place_atop\", right, send -> {\n                if (send)\n                    PacketHandler.sendToServer(new PacketToggleBlockPlacement());\n\n                return GadgetBuilding.shouldPlaceAtop(this.getGadget());\n            }));\n        }\n        addRenderableWidget(new PositionedIconActionable(RadialTranslation.ANCHOR, \"anchor\", left, send -> {\n            if (send)\n                PacketHandler.sendToServer(new PacketAnchor());\n\n            ItemStack stack = this.getGadget();\n            if (stack.getItem() instanceof GadgetCopyPaste || stack.getItem() instanceof GadgetDestruction) {\n                return ((AbstractGadget) stack.getItem()).getAnchor(stack) != null;\n            }\n\n            return GadgetUtils.getAnchor(stack).isPresent();\n        }));\n\n        if (!(tool.getItem() instanceof GadgetExchanger)) {\n            addRenderableWidget(new PositionedIconActionable(RadialTranslation.UNDO, \"undo\", left, false, send -> {\n                if (send)\n                    PacketHandler.sendToServer(new PacketUndo());\n\n                return false;\n            }));\n        }\n\n        this.updateButtons(tool);\n    }\n\n    private void updateButtons(ItemStack tool) {\n        int posRight = 0;\n        int posLeft = 0;\n        int dim = 24;\n        int padding = 10;\n        boolean isDestruction = tool.getItem() instanceof GadgetDestruction;\n        ScreenPosition right = isDestruction ? ScreenPosition.BOTTOM : ScreenPosition.RIGHT;\n        for (GuiEventListener widget : children()) {\n            if (!(widget instanceof PositionedIconActionable))\n                continue;\n\n            PositionedIconActionable button = (PositionedIconActionable) widget;\n\n            if (!button.visible) {\n                continue;\n            }\n            int offset;\n            boolean isRight = button.position == right;\n            if (isRight) {\n                posRight += dim + padding;\n                offset = 70;\n            } else {\n                posLeft += dim + padding;\n                offset = -70 - dim;\n            }\n            button.setWidth(dim);\n            button.setHeight(dim);\n            if (isDestruction)\n                button.setY(height / 2 + (isRight ? 10 : -button.getHeight() - 10));\n            else\n                button.setX(width / 2 + offset);\n        }\n        posRight = resetPos(tool, padding, posRight);\n        posLeft = resetPos(tool, padding, posLeft);\n        for (GuiEventListener widget : children()) {\n            if (!(widget instanceof PositionedIconActionable))\n                continue;\n\n            PositionedIconActionable button = (PositionedIconActionable) widget;\n            if (!button.visible) {\n                continue;\n            }\n            boolean isRight = button.position == right;\n            int pos = isRight\n                    ? posRight\n                    : posLeft;\n            if (isDestruction) {\n                button.setX(pos);\n            } else {\n                button.setY(pos);\n            }\n\n            if (isRight) {\n                posRight += dim + padding;\n            } else {\n                posLeft += dim + padding;\n            }\n        }\n    }\n\n    private int resetPos(ItemStack tool, int padding, int pos) {\n        return tool.getItem() instanceof GadgetDestruction\n                ? this.width / 2 - (pos - padding) / 2\n                : this.height / 2 - (pos - padding) / 2;\n    }\n\n    private ItemStack getGadget() {\n        assert this.getMinecraft().player != null;\n        return AbstractGadget.getGadget(this.getMinecraft().player);\n    }\n\n    @Override\n    public void render(PoseStack matrices, int mx, int my, float partialTicks) {\n        float stime = 5F;\n        float fract = Math.min(stime, this.timeIn + partialTicks) / stime;\n        int x = this.width / 2;\n        int y = this.height / 2;\n\n        int radiusMin = 26;\n        int radiusMax = 60;\n        double dist = new Vec3(x, y, 0).distanceTo(new Vec3(mx, my, 0));\n        boolean inRange = false;\n        if (this.segments != 0) {\n            inRange = dist > radiusMin && dist < radiusMax;\n            for (GuiEventListener button : children()) {\n                if (button instanceof PositionedIconActionable) {\n                    ((PositionedIconActionable) button).setFaded(inRange);\n                }\n            }\n        }\n\n        // This triggers the animation on creation\n        matrices.pushPose();\n        matrices.translate((1 - fract) * x, (1 - fract) * y, 0);\n        matrices.scale(fract, fract, fract);\n        super.render(matrices, mx, my, partialTicks);\n        matrices.popPose();\n\n        if (this.segments == 0) {\n            return;\n        }\n\n        float angle = mouseAngle(x, y, mx, my);\n\n        float totalDeg = 0;\n        float degPer = 360F / this.segments;\n\n        List<NameDisplayData> nameData = new ArrayList<>();\n\n        ItemStack tool = this.getGadget();\n        if (tool.isEmpty()) {\n            return;\n        }\n\n        this.slotSelected = -1;\n\n        List<ResourceLocation> signs;\n        int modeIndex;\n        if (tool.getItem() instanceof GadgetBuilding) {\n            modeIndex = GadgetBuilding.getToolMode(tool).ordinal();\n            signs = Arrays.stream(BuildingModes.values()).map(e -> new ResourceLocation(Reference.MODID, e.getIcon())).collect(Collectors.toList());\n        } else if (tool.getItem() instanceof GadgetExchanger) {\n            modeIndex = GadgetExchanger.getToolMode(tool).ordinal();\n            signs = Arrays.stream(ExchangingModes.values()).map(e -> new ResourceLocation(Reference.MODID, e.getIcon())).collect(Collectors.toList());\n        } else {\n            modeIndex = GadgetCopyPaste.getToolMode(tool).ordinal();\n            signs = signsCopyPaste;\n        }\n\n        boolean shouldCenter = (this.segments + 2) % 4 == 0;\n        int indexBottom = this.segments / 4;\n        int indexTop = indexBottom + this.segments / 2;\n        for (int seg = 0; seg < this.segments; seg++) {\n            boolean mouseInSector = this.isCursorInSlice(angle, totalDeg, degPer, inRange);\n            float radius = Math.max(0F, Math.min((this.timeIn + partialTicks - seg * 6F / this.segments) * 40F, radiusMax));\n\n            float gs = 0.25F;\n            if (seg % 2 == 0) {\n                gs += 0.1F;\n            }\n\n            float r = gs;\n            float g = gs + (seg == modeIndex\n                    ? 1F\n                    : 0.0F);\n            float b = gs;\n            float a = 0.4F;\n            if (mouseInSector) {\n                this.slotSelected = seg;\n                r = g = b = 1F;\n            }\n\n            MultiBufferSource.BufferSource bufferSource = Minecraft.getInstance().renderBuffers().bufferSource();\n            VertexConsumer buffer = bufferSource.getBuffer(OurRenderTypes.TRIANGLE_STRIP);\n\n            for (float i = degPer; i >= 0; i--) {\n                float rad = (float) ((i + totalDeg) / 180F * Math.PI);\n                float xp = (float) (x + Math.cos(rad) * radius);\n                float yp = (float) (y + Math.sin(rad) * radius);\n                if ((int) i == (int) (degPer / 2))\n                    nameData.add(new NameDisplayData((int) xp, (int) yp, mouseInSector, shouldCenter && (seg == indexBottom || seg == indexTop)));\n\n                Matrix4f pose = matrices.last().pose();\n                buffer.vertex(pose, (float) (x + Math.cos(rad) * radius / 2.3F), (float) (y + Math.sin(rad) * radius / 2.3F), 0).color(r, g, b, a).endVertex();\n                buffer.vertex(xp, yp, 0).color(r, g, b, a).endVertex();\n            }\n\n            bufferSource.endBatch(OurRenderTypes.TRIANGLE_STRIP);\n            totalDeg += degPer;\n        }\n\n        // This is the naming logic for the text that pops up\n        for (int i = 0; i < nameData.size(); i++) {\n            matrices.pushPose();\n            NameDisplayData data = nameData.get(i);\n            int xp = data.getX();\n            int yp = data.getY();\n\n            String name;\n            if (tool.getItem() instanceof GadgetBuilding) {\n                name = ForgeI18n.getPattern(BuildingModes.values()[i].getTranslationKey());\n            } else if (tool.getItem() instanceof GadgetExchanger) {\n                name = ForgeI18n.getPattern(ExchangingModes.values()[i].getTranslationKey());\n            } else {\n                name = GadgetCopyPaste.ToolMode.values()[i].getTranslation().format();\n            }\n\n            int xsp = xp - 4;\n            int ysp = yp;\n            int width = font.width(name);\n\n            if (xsp < x) {\n                xsp -= width - 8;\n            }\n            if (ysp < y) {\n                ysp -= 9;\n            }\n\n            Color color = i == modeIndex ? Color.GREEN : Color.WHITE;\n            if (data.isSelected())\n                font.drawShadow(matrices, name, xsp + (data.isCentralized() ? width / 2f - 4 : 0), ysp, color.getRGB());\n\n            double mod = 0.7;\n            int xdp = (int) ((xp - x) * mod + x);\n            int ydp = (int) ((yp - y) * mod + y);\n\n            RenderSystem.setShader(GameRenderer::getPositionTexShader);\n            RenderSystem.setShaderColor(color.getRed() / 255F, color.getGreen() / 255F, color.getBlue() / 255F, 1);\n            RenderSystem.setShaderTexture(0, signs.get(i));\n            blit(matrices, xdp - 8, ydp - 8, 0, 0, 16, 16, 16, 16);\n\n            matrices.popPose();\n        }\n\n        float s = 1.8F * fract;\n        PoseStack stack = RenderSystem.getModelViewStack();\n        stack.pushPose();\n        stack.scale(s, s, s);\n        matrices.popPose();\n        stack.translate(x / s - (tool.getItem() instanceof GadgetCopyPaste ? 8 : 8.5), y / s - 8, 0);\n        this.itemRenderer.renderAndDecorateItem(stack, tool, 0, 0);\n        stack.popPose();\n    }\n\n    private boolean isCursorInSlice(float angle, float totalDeg, float degPer, boolean inRange) {\n        return inRange && angle > totalDeg && angle < totalDeg + degPer;\n    }\n\n    private void changeMode() {\n        if (this.slotSelected >= 0) {\n            Item gadget = this.getGadget().getItem();\n\n            // This should logically never fail but implementing a way to ensure that would\n            // be a pretty solid idea for the next guy to touch this code.\n            String mode;\n            if (gadget instanceof GadgetBuilding) {\n                mode = ForgeI18n.getPattern(BuildingModes.values()[this.slotSelected].getTranslationKey());\n            } else if (gadget instanceof GadgetExchanger) {\n                mode = ForgeI18n.getPattern(ExchangingModes.values()[this.slotSelected].getTranslationKey());\n            } else {\n                mode = GadgetCopyPaste.ToolMode.values()[this.slotSelected].getTranslation().format();\n            }\n\n            assert getMinecraft().player != null;\n            getMinecraft().player.displayClientMessage(MessageTranslation.MODE_SET.componentTranslation(mode).setStyle(Styles.AQUA), true);\n\n            PacketHandler.sendToServer(new PacketToggleMode(this.slotSelected));\n            OurSounds.playSound(OurSounds.BEEP.get());\n        }\n    }\n\n    @Override\n    public boolean mouseClicked(double mouseX, double mouseY, int mouseButton) {\n        this.changeMode();\n        return super.mouseClicked(mouseX, mouseY, mouseButton);\n    }\n\n    @Override\n    public void tick() {\n        if (!InputConstants.isKeyDown(Minecraft.getInstance().getWindow().getWindow(), KeyBindings.menuSettings.getKey().getValue())) {\n            onClose();\n            changeMode();\n        }\n\n        ImmutableSet<KeyMapping> set = ImmutableSet.of(getMinecraft().options.keyUp, getMinecraft().options.keyLeft, getMinecraft().options.keyDown, getMinecraft().options.keyRight, getMinecraft().options.keyShift, getMinecraft().options.keySprint, getMinecraft().options.keyJump);\n        for (KeyMapping k : set)\n            KeyMapping.set(k.getKey(), k.isDown());\n\n        this.timeIn++;\n        ItemStack tool = this.getGadget();\n        boolean builder = tool.getItem() instanceof GadgetBuilding;\n        if (!builder && !(tool.getItem() instanceof GadgetExchanger)) {\n            return;\n        }\n\n        boolean curent;\n        boolean changed = false;\n        for (int i = 0; i < this.conditionalButtons.size(); i++) {\n            Button button = this.conditionalButtons.get(i);\n            if (builder) {\n                curent = GadgetBuilding.getToolMode(tool) == BuildingModes.SURFACE;\n            } else {\n                curent = i == 0 || GadgetExchanger.getToolMode(tool) == ExchangingModes.SURFACE;\n            }\n\n            if (button.visible != curent) {\n                button.visible = curent;\n                changed = true;\n            }\n        }\n        if (changed) {\n            this.updateButtons(tool);\n        }\n    }\n\n    @Override\n    public boolean isPauseScreen() {\n        return false;\n    }\n\n    private void sendRangeUpdate(int valueNew) {\n        if (valueNew != GadgetUtils.getToolRange(this.getGadget())) {\n            PacketHandler.sendToServer(new PacketChangeRange(valueNew));\n        }\n    }\n\n    public enum ScreenPosition {\n        RIGHT, LEFT, BOTTOM, TOP\n    }\n\n    private static final class NameDisplayData {\n        private final int x;\n        private final int y;\n        private final boolean selected;\n        private final boolean centralize;\n\n        private NameDisplayData(int x, int y, boolean selected, boolean centralize) {\n            this.x = x;\n            this.y = y;\n            this.selected = selected;\n            this.centralize = centralize;\n        }\n\n        private int getX() {\n            return this.x;\n        }\n\n        private int getY() {\n            return this.y;\n        }\n\n        private boolean isSelected() {\n            return this.selected;\n        }\n\n        private boolean isCentralized() {\n            return this.centralize;\n        }\n    }\n\n    private static class PositionedIconActionable extends GuiIconActionable {\n        private ScreenPosition position;\n\n        PositionedIconActionable(RadialTranslation message, String icon, ScreenPosition position, boolean isSelectable, Predicate<Boolean> action) {\n            super(0, 0, icon, message.componentTranslation(), isSelectable, action);\n\n            this.position = position;\n        }\n\n        PositionedIconActionable(RadialTranslation message, String icon, ScreenPosition position, Predicate<Boolean> action) {\n            this(message, icon, position, true, action);\n        }\n    }\n\n    private static class Vector2f {\n        public float x;\n        public float y;\n\n        public Vector2f(float x, float y) {\n            this.x = x;\n            this.y = y;\n        }\n\n        public final float dot(Vector2f v1) {\n            return (this.x * v1.x + this.y * v1.y);\n        }\n\n        public final float length() {\n            return (float) Math.sqrt(this.x * this.x + this.y * this.y);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/client/screen/PasteGUI.java",
    "content": "/**\n * Parts of this class were adapted from code written by TTerrag for the Chisel mod: https://github.com/Chisel-Team/Chisel\n * Chisel is Open Source and distributed under GNU GPL v2\n */\n\npackage com.direwolf20.buildinggadgets.client.screen;\n\nimport com.direwolf20.buildinggadgets.client.screen.widgets.GuiIncrementer;\nimport com.direwolf20.buildinggadgets.common.items.GadgetCopyPaste;\nimport com.direwolf20.buildinggadgets.common.network.PacketHandler;\nimport com.direwolf20.buildinggadgets.common.network.packets.PacketPasteGUI;\nimport com.direwolf20.buildinggadgets.common.util.lang.GuiTranslation;\nimport com.mojang.blaze3d.vertex.PoseStack;\nimport net.minecraft.client.Minecraft;\nimport net.minecraft.client.gui.components.AbstractButton;\nimport net.minecraft.client.gui.screens.Screen;\nimport net.minecraft.client.resources.language.I18n;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.world.item.ItemStack;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class PasteGUI extends Screen {\n    private GuiIncrementer X, Y, Z;\n    private List<GuiIncrementer> fields = new ArrayList<>();\n    private ItemStack copyPasteTool;\n\n    PasteGUI(ItemStack tool) {\n        super(Component.literal(\"\"));\n        this.copyPasteTool = tool;\n    }\n\n    @Override\n    public void init() {\n        super.init();\n\n        int x = width / 2;\n        int y = height / 2;\n\n        fields.add(X = new GuiIncrementer(x - (GuiIncrementer.WIDTH + (GuiIncrementer.WIDTH / 2)) - 10, y - 10, -16, 16, this::onChange));\n        fields.add(Y = new GuiIncrementer(x - GuiIncrementer.WIDTH / 2, y - 10, -16, 16, this::onChange));\n        fields.add(Z = new GuiIncrementer(x + (GuiIncrementer.WIDTH / 2) + 10, y - 10, -16, 16, this::onChange));\n\n        BlockPos currentOffset = GadgetCopyPaste.getRelativeVector(this.copyPasteTool);\n        X.setValue(currentOffset.getX());\n        Y.setValue(currentOffset.getY());\n        Z.setValue(currentOffset.getZ());\n\n        List<AbstractButton> buttons = new ArrayList<AbstractButton>() {{\n            add(new CopyGUI.CenteredButton(y + 20, 70, GuiTranslation.SINGLE_CONFIRM.componentTranslation(), (button) -> {\n                PacketHandler.sendToServer(new PacketPasteGUI(X.getValue(), Y.getValue(), Z.getValue()));\n                onClose();\n            }));\n\n            add(new CopyGUI.CenteredButton(y + 20, 40, GuiTranslation.SINGLE_RESET.componentTranslation(), (button) -> {\n                X.setValue(0);\n                Y.setValue(0);\n                Z.setValue(0);\n                sendPacket();\n            }));\n        }};\n\n        CopyGUI.CenteredButton.centerButtonList(buttons, x);\n\n        buttons.forEach(this::addRenderableWidget);\n        fields.forEach(this::addRenderableWidget);\n    }\n\n    private void sendPacket() {\n        PacketHandler.sendToServer(new PacketPasteGUI(X.getValue(), Y.getValue(), Z.getValue()));\n    }\n\n    private void onChange(int value) {\n        PacketHandler.sendToServer(new PacketPasteGUI(X.getValue(), Y.getValue(), Z.getValue()));\n    }\n\n    @Override\n    public boolean keyPressed(int mouseX, int mouseY, int __unused) {\n        fields.forEach(button -> button.keyPressed(mouseX, mouseY, __unused));\n        return super.keyPressed(mouseX, mouseY, __unused);\n    }\n\n    @Override\n    public boolean charTyped(char charTyped, int __unused) {\n        fields.forEach(button -> button.charTyped(charTyped, __unused));\n        return false;\n    }\n\n    @Override\n    public boolean isPauseScreen() {\n        return false;\n    }\n\n    @Override\n    public void render(PoseStack matrices, int mouseX, int mouseY, float partialTicks) {\n        drawLabel(matrices, \"X\", -75);\n        drawLabel(matrices, \"Y\", 0);\n        drawLabel(matrices, \"Z\", 75);\n\n        drawCenteredString(matrices, Minecraft.getInstance().font, I18n.get(GuiTranslation.COPY_LABEL_HEADING.getTranslationKey()), (int) (width / 2f), (int) (height / 2f) - 60, 0xFFFFFF);\n\n        super.render(matrices, mouseX, mouseY, partialTicks);\n    }\n\n    private void drawLabel(PoseStack matrices, String name, int x) {\n        font.drawShadow(matrices, name, (width / 2f) + x, (height / 2f) - 30, 0xFFFFFF);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/client/screen/ScrollingMaterialList.java",
    "content": "package com.direwolf20.buildinggadgets.client.screen;\n\nimport com.direwolf20.buildinggadgets.client.screen.widgets.EntryList;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.IItemIndex;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.InventoryHelper;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.MatchResult;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.materials.MaterialList;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.materials.objects.IUniqueObject;\nimport com.direwolf20.buildinggadgets.common.util.lang.ITranslationProvider;\nimport com.direwolf20.buildinggadgets.common.util.lang.MaterialListTranslation;\nimport com.google.common.collect.ImmutableMultiset;\nimport com.google.common.collect.Iterators;\nimport com.google.common.collect.Multiset;\nimport com.mojang.blaze3d.platform.Lighting;\nimport com.mojang.blaze3d.vertex.PoseStack;\nimport net.minecraft.client.Minecraft;\nimport net.minecraft.client.gui.components.ObjectSelectionList;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.util.Mth;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.item.ItemStack;\nimport org.lwjgl.glfw.GLFW;\n\nimport java.awt.*;\nimport java.util.Comparator;\nimport java.util.Iterator;\n\nimport static com.direwolf20.buildinggadgets.client.screen.MaterialListGUI.*;\nimport static com.direwolf20.buildinggadgets.client.screen.ScrollingMaterialList.Entry;\n\n// Todo change to AbstractList as it's an easy fix compared to duping the class\nclass ScrollingMaterialList extends EntryList<Entry> {\n    private static final int UPDATE_MILLIS = 1000;\n    static final int TOP = 16;\n    static final int BOTTOM = 32;\n\n    private static final int SLOT_SIZE = 18;\n    private static final int MARGIN = 2;\n    private static final int ENTRY_HEIGHT = Math.max(SLOT_SIZE + MARGIN * 2, Minecraft.getInstance().font.lineHeight * 2 + MARGIN * 3);\n    private static final int LINE_SIDE_MARGIN = 8;\n\n    private MaterialListGUI gui;\n\n    private SortingModes sortingMode;\n    private long lastUpdate;\n    private Iterator<ImmutableMultiset<IUniqueObject<?>>> multisetIterator;\n\n    public ScrollingMaterialList(MaterialListGUI gui) {\n        super(gui.getWindowLeftX(), gui.getWindowTopY() + TOP, gui.getWindowWidth(), gui.getWindowHeight() - TOP - BOTTOM, ENTRY_HEIGHT);\n\n        this.gui = gui;\n        this.setSortingMode(SortingModes.NAME);\n\n        updateEntries();\n    }\n\n    private void updateEntries() {\n        this.lastUpdate = System.currentTimeMillis();\n        this.clearEntries();\n\n        if (multisetIterator == null || !multisetIterator.hasNext()) {\n            MaterialList list = gui.getHeader().getRequiredItems();\n            multisetIterator = list != null ? list.iterator() : Iterators.singletonIterator(ImmutableMultiset.of());\n        }\n\n        Player player = Minecraft.getInstance().player;\n\n        // Could likely just assert\n        if( player == null )\n            return;\n\n        IItemIndex index = InventoryHelper.index(gui.getTemplateItem(), player);\n        MatchResult result = index.tryMatch(multisetIterator.next());\n\n        for (Multiset.Entry<IUniqueObject<?>> entry : result.getChosenOption().entrySet()) {\n            IUniqueObject<?> item = entry.getElement();\n            addEntry(new Entry(this, item, entry.getCount(), result.getFoundItems().count(entry.getElement())));\n        }\n\n        sort();\n    }\n\n    @Override\n    protected int getScrollbarPosition() {\n        return getRight() - MARGIN - SCROLL_BAR_WIDTH;\n    }\n\n    @Override\n    public boolean keyPressed(int keyCode, int scanCode, int modifiers) {\n        if (keyCode == GLFW.GLFW_KEY_E) {\n            assert Minecraft.getInstance().player != null;\n\n            Minecraft.getInstance().player.closeContainer();\n            return true;\n        }\n        return super.keyPressed(keyCode, scanCode, modifiers);\n    }\n\n    @Override\n    public void render(PoseStack matrices, int mouseX, int mouseY, float partialTicks) {\n        if (lastUpdate + UPDATE_MILLIS < System.currentTimeMillis())\n            updateEntries();\n\n        super.render(matrices, mouseX, mouseY, partialTicks);\n    }\n\n    public void reset() {\n        multisetIterator = null;\n    }\n\n    static class Entry extends ObjectSelectionList.Entry<Entry> {\n\n        private ScrollingMaterialList parent;\n        private int required;\n        private int available;\n\n        private ItemStack stack;\n\n        private String itemName;\n        private String amount;\n\n        private int widthItemName;\n        private int widthAmount;\n\n        public Entry(ScrollingMaterialList parent, IUniqueObject<?> item, int required, int available) {\n            this.parent = parent;\n            this.required = required;\n            this.available = Mth.clamp(available, 0, required);\n\n            this.stack = item.createStack();\n            this.itemName = stack.getHoverName().getString();\n\n            // Use this.available since the parameter is not clamped\n            this.amount = this.available + \"/\" + required;\n            this.widthItemName = Minecraft.getInstance().font.width(itemName);\n            this.widthAmount = Minecraft.getInstance().font.width(amount);\n        }\n\n        @Override\n        public void render(PoseStack matrices, int index, int topY, int leftX, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float particleTicks) {\n            // Weird render issue with GuiSlot where the right border is slightly offset\n            // MARGIN * 2 is just a magic number that made it look nice\n            int right = leftX + entryWidth - MARGIN * 2;\n            // Centralize entry vertically, for some reason this.getY() is not inclusive on the bottom\n            int bottom = topY + entryHeight;\n\n            int slotX = leftX + MARGIN;\n            int slotY = topY + MARGIN;\n\n            matrices.pushPose();\n            drawIcon(matrices, stack, slotX, slotY);\n            drawTextOverlay(matrices, right, topY, bottom, slotX);\n            drawHoveringText(stack, slotX, slotY, mouseX, mouseY);\n            matrices.popPose();\n        }\n\n        private void drawTextOverlay(PoseStack matrices, int right, int top, int bottom, int slotX) {\n            int itemNameX = slotX + SLOT_SIZE + MARGIN;\n            // -1 because the bottom x coordinate is exclusive\n            renderTextVerticalCenter(matrices, itemName, itemNameX, top, bottom, Color.WHITE.getRGB());\n            renderTextHorizontalRight(matrices, amount, right, getYForAlignedCenter(top, bottom, Minecraft.getInstance().font.lineHeight), getTextColor());\n        }\n\n        private void drawHoveringText(ItemStack item, int slotX, int slotY, int mouseX, int mouseY) {\n            if (isPointInBox(mouseX, mouseY, slotX, slotY, 18, 18))\n                parent.gui.setTaskHoveringText(mouseX, mouseY, parent.gui.getTooltipFromItem(item));\n        }\n\n        private void drawIcon(PoseStack matrices, ItemStack item, int slotX, int slotY) {\n            Lighting.setupFor3DItems();\n            Minecraft.getInstance().getItemRenderer().renderAndDecorateItem(matrices, item, slotX, slotY);\n//            RenderSystem.color3f(1, 1, 1);\n            Lighting.setupForFlatItems();\n        }\n\n        private boolean hasEnoughItems() {\n            return required == available;\n        }\n\n        private int getTextColor() {\n            return hasEnoughItems() ? Color.GREEN.getRGB() : Color.RED.getRGB();\n        }\n\n        public int getRequired() {\n            return required;\n        }\n\n        public int getAvailable() {\n            return available;\n        }\n\n        public int getMissing() {\n            return required - available;\n        }\n\n        public ItemStack getStack() {\n            return stack;\n        }\n\n        public String getItemName() {\n            return itemName;\n        }\n\n        public String getFormattedRequired() {\n            int maxSize = stack.getMaxStackSize();\n            int stacks = required / maxSize; // Integer division automatically floors\n            int leftover = required % maxSize;\n            if (stacks == 0)\n                return String.valueOf(leftover);\n            return stacks + \"×\" + maxSize + \"+\" + leftover;\n        }\n\n        @Override\n        public boolean mouseClicked(double x, double y, int button) {\n            // TODO add replacement function and make entries selectable\n//            if (isMouseOver(x, y)) {\n//                parent.setSelected(this);\n//                return true;\n//            }\n            return false;\n        }\n\n        public boolean isSelected() {\n            return parent.getSelected() == this;\n        }\n\n        @Override\n        public Component getNarration() {\n            return null;\n        }\n    }\n\n    public SortingModes getSortingMode() {\n        return sortingMode;\n    }\n\n    public void setSortingMode(SortingModes sortingMode) {\n        this.sortingMode = sortingMode;\n        sort();\n    }\n\n    private void sort() {\n        children().sort(sortingMode.getComparator());\n    }\n\n    enum SortingModes {\n\n        NAME(Comparator.comparing(Entry::getItemName), MaterialListTranslation.BUTTON_SORTING_NAMEAZ),\n        NAME_REVERSED(NAME.getComparator().reversed(), MaterialListTranslation.BUTTON_SORTING_NAMEZA),\n        REQUIRED(Comparator.comparingInt(Entry::getRequired), MaterialListTranslation.BUTTON_SORTING_REQUIREDACSE),\n        REQUIRED_REVERSED(REQUIRED.getComparator().reversed(), MaterialListTranslation.BUTTON_SORTING_MISSINGDESC),\n        MISSING(Comparator.comparingInt(Entry::getMissing), MaterialListTranslation.BUTTON_SORTING_MISSINGACSE),\n        MISSING_REVERSED(MISSING.getComparator().reversed(), MaterialListTranslation.BUTTON_SORTING_MISSINGDESC);\n\n        private final Comparator<Entry> comparator;\n        private final ITranslationProvider translationProvider;\n\n        SortingModes(Comparator<Entry> comparator, ITranslationProvider provider) {\n            this.comparator = comparator;\n            this.translationProvider = provider;\n        }\n\n        public Comparator<Entry> getComparator() {\n            return comparator;\n        }\n\n        public String getLocalizedName() {\n            return translationProvider.format();\n        }\n\n        public ITranslationProvider getTranslationProvider() {\n            return translationProvider;\n        }\n\n        public SortingModes next() {\n            int nextIndex = ordinal() + 1;\n            return VALUES[nextIndex >= VALUES.length ? 0 : nextIndex];\n        }\n\n        public static final SortingModes[] VALUES = SortingModes.values();\n\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/client/screen/TemplateManagerGUI.java",
    "content": "/**\n * Parts of this class were adapted from code written by TTerrag for the Chisel mod: https://github.com/Chisel-Team/Chisel\n * Chisel is Open Source and distributed under GNU GPL v2\n */\n\npackage com.direwolf20.buildinggadgets.client.screen;\n\nimport com.direwolf20.buildinggadgets.common.BuildingGadgets;\nimport com.direwolf20.buildinggadgets.common.capability.CapabilityTemplate;\nimport com.direwolf20.buildinggadgets.common.containers.TemplateManagerContainer;\nimport com.direwolf20.buildinggadgets.common.items.OurItems;\nimport com.direwolf20.buildinggadgets.common.network.PacketHandler;\nimport com.direwolf20.buildinggadgets.common.network.packets.PacketTemplateManagerTemplateCreated;\nimport com.direwolf20.buildinggadgets.common.tainted.building.PlacementTarget;\nimport com.direwolf20.buildinggadgets.common.tainted.building.view.BuildContext;\nimport com.direwolf20.buildinggadgets.common.tainted.building.view.IBuildView;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.InventoryHelper;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.MatchResult;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.materials.MaterialList;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.materials.objects.IUniqueObject;\nimport com.direwolf20.buildinggadgets.common.tainted.template.*;\nimport com.direwolf20.buildinggadgets.common.tainted.template.ITemplateProvider.IUpdateListener;\nimport com.direwolf20.buildinggadgets.common.tileentities.TemplateManagerTileEntity;\nimport com.direwolf20.buildinggadgets.common.util.GadgetUtils;\nimport com.direwolf20.buildinggadgets.common.util.exceptions.TemplateParseException.IllegalMinecraftVersionException;\nimport com.direwolf20.buildinggadgets.common.util.exceptions.TemplateParseException.UnknownTemplateVersionException;\nimport com.direwolf20.buildinggadgets.common.util.exceptions.TemplateReadException;\nimport com.direwolf20.buildinggadgets.common.util.exceptions.TemplateReadException.CorruptJsonException;\nimport com.direwolf20.buildinggadgets.common.util.exceptions.TemplateWriteException.DataCannotBeWrittenException;\nimport com.direwolf20.buildinggadgets.common.util.lang.GuiTranslation;\nimport com.direwolf20.buildinggadgets.common.util.lang.MessageTranslation;\nimport com.direwolf20.buildinggadgets.common.util.lang.Styles;\nimport com.direwolf20.buildinggadgets.common.util.ref.Reference;\nimport com.google.common.collect.ImmutableList;\nimport com.google.common.collect.ImmutableMultiset;\nimport com.google.common.collect.Lists;\nimport com.google.common.collect.Multiset;\nimport com.google.gson.JsonParseException;\nimport com.mojang.blaze3d.platform.Lighting;\nimport com.mojang.blaze3d.systems.RenderSystem;\nimport com.mojang.blaze3d.vertex.BufferBuilder;\nimport com.mojang.blaze3d.vertex.DefaultVertexFormat;\nimport com.mojang.blaze3d.vertex.PoseStack;\nimport com.mojang.blaze3d.vertex.VertexFormat;\nimport com.mojang.brigadier.exceptions.CommandSyntaxException;\nimport net.minecraft.SharedConstants;\nimport net.minecraft.client.Minecraft;\nimport net.minecraft.client.gui.components.Button;\nimport net.minecraft.client.gui.components.EditBox;\nimport net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;\nimport net.minecraft.client.renderer.Rect2i;\nimport net.minecraft.client.renderer.block.BlockRenderDispatcher;\nimport net.minecraft.client.renderer.blockentity.BlockEntityRenderer;\nimport net.minecraft.client.resources.model.BakedModel;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.nbt.TagParser;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.resources.ResourceLocation;\nimport net.minecraft.world.entity.player.Inventory;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.inventory.InventoryMenu;\nimport net.minecraft.world.inventory.Slot;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.TooltipFlag;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.RenderShape;\nimport net.minecraft.world.level.block.entity.BlockEntity;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraftforge.common.util.LazyOptional;\n\nimport java.awt.*;\nimport java.util.Comparator;\nimport java.util.List;\nimport java.util.Random;\n\npublic class TemplateManagerGUI extends AbstractContainerScreen<TemplateManagerContainer> {\n    private static final ResourceLocation background = new ResourceLocation(Reference.MODID, \"textures/gui/template_manager.png\");\n\n    private final Rect2i panel = new Rect2i((8 - 20), 12, 136, 80);\n    private boolean panelClicked;\n    private int clickButton, clickX, clickY;\n    private float initRotX, initRotY, initZoom, initPanX, initPanY;\n    private float momentumX, momentumY;\n    private float rotX = 0, rotY = 0, zoom = 1;\n    private float panX = 0, panY = 0;\n\n    private EditBox nameField;\n    private Button buttonSave, buttonLoad, buttonCopy, buttonPaste;\n\n    private final TemplateManagerTileEntity te;\n    private final TemplateManagerContainer container;\n    private final LazyOptional<ITemplateProvider> templateProvider = getWorld().getCapability(CapabilityTemplate.TEMPLATE_PROVIDER_CAPABILITY);\n\n    // It is so stupid I can't get the key from the template.\n    private Template template;\n\n    public TemplateManagerGUI(TemplateManagerContainer container, Inventory playerInventory, Component title) {\n        super(container, playerInventory, Component.literal(\"\"));\n\n        this.container = container;\n        this.te = container.getTe();\n    }\n\n    @Override\n    public void init() {\n        super.init();\n        this.nameField = new EditBox(this.font, (this.leftPos - 20) + 8, topPos - 5, imageWidth - 16, this.font.lineHeight + 3, GuiTranslation.TEMPLATE_NAME_TIP.componentTranslation());\n\n        int x = (leftPos - 20) + 180;\n        buttonSave = addRenderableWidget(Button.builder(GuiTranslation.BUTTON_SAVE.componentTranslation(), b -> onSave()).pos(x, topPos + 17).size(60, 20).build());\n        buttonLoad = addRenderableWidget(Button.builder(GuiTranslation.BUTTON_LOAD.componentTranslation(), b -> onLoad()).pos(x, topPos + 39).size(60, 20).build());\n        buttonCopy = addRenderableWidget(Button.builder(GuiTranslation.BUTTON_COPY.componentTranslation(), b -> onCopy()).pos(x, topPos + 66).size(60, 20).build());\n        buttonPaste = addRenderableWidget(Button.builder(GuiTranslation.BUTTON_PASTE.componentTranslation(), b -> onPaste()).pos(x, topPos + 89).size(60, 20).build());\n\n        this.nameField.setMaxLength(50);\n        this.nameField.setVisible(true);\n        addRenderableWidget(nameField);\n    }\n\n    @Override\n    public void render(PoseStack matrices, int mouseX, int mouseY, float partialTicks) {\n        super.render(matrices, mouseX, mouseY, partialTicks);\n        this.renderTooltip(matrices, mouseX, mouseY);\n\n        drawString(matrices, font, \"Preview disabled for now...\", leftPos - 10, topPos + 40, 0xFFFFFF);\n        if (this.template != null) {\n            renderRequirement(matrices, mouseX, mouseY);\n        }\n\n//        validateCache(partialTicks);\n    }\n\n    @Override\n    protected void renderBg(PoseStack matrices, float partialTicks, int mouseX, int mouseY) {\n        renderBackground(matrices);\n\n        RenderSystem.setShaderTexture(0, background);\n        blit(matrices, leftPos - 20, topPos - 12, 0, 0, imageWidth, imageHeight + 25);\n        blit(matrices, (leftPos - 20) + imageWidth, topPos + 8, imageWidth + 3, 30, 71, imageHeight);\n\n        if (!buttonCopy.isHoveredOrFocused() && !buttonPaste.isHoveredOrFocused()) {\n            if (buttonLoad.isHoveredOrFocused())\n                blit(matrices, (leftPos + imageWidth) - 44, topPos + 38, imageWidth, 0, 17, 24);\n            else\n                blit(matrices, (leftPos + imageWidth) - 44, topPos + 38, imageWidth + 17, 0, 16, 24);\n        }\n\n        this.nameField.render(matrices, mouseX, mouseY, partialTicks);\n//        fill(matrices, guiLeft + panel.getX() - 1, guiTop + panel.getY() - 1, guiLeft + panel.getX() + panel.getWidth() + 1, guiTop + panel.getY() + panel.getHeight() + 1, 0xFF8A8A8A);\n\n        if (this.template != null) {\n            renderPanel();\n        }\n    }\n\n    private void validateCache(float partialTicks) {\n        // Invalidate the render\n        if (container.getSlot(0).getItem().isEmpty() && template != null) {\n            template = null;\n            resetViewport();\n            return;\n        }\n\n        container.getSlot(0).getItem().getCapability(CapabilityTemplate.TEMPLATE_KEY_CAPABILITY).ifPresent(key -> templateProvider.ifPresent(provider -> {\n            // Make sure we're not re-creating the same cache.\n            Template template = provider.getTemplateForKey(key);\n            if (this.template == template)\n                return;\n\n            this.template = template;\n\n//            IBuildView view = template.createViewInContext(\n//                    SimpleBuildContext.builder()\n//                            .player(getMinecraft().player)\n//                            .stack(container.getSlot(0).getStack())\n//                            .build(new MockDelegationWorld(getMinecraft().world)));\n\n//            int displayList = GLAllocation.generateDisplayLists(1);\n//            GlStateManager.newList(displayList, GL11.GL_COMPILE);\n\n//            renderStructure(view, partialTicks);\n\n//            GlStateManager.endList();\n//            this.displayList = displayList;\n        }));\n    }\n\n    private void renderStructure(IBuildView view, float partialTicks) {\n        Random rand = new Random();\n        BlockRenderDispatcher dispatcher = getMinecraft().getBlockRenderer();\n\n        BufferBuilder bufferBuilder = new BufferBuilder(2097152);\n        bufferBuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.BLOCK);\n\n        for (PlacementTarget target : view) {\n            target.placeIn(view.getContext());\n            BlockPos targetPos = target.getPos();\n            BlockState renderBlockState = view.getContext().getWorld().getBlockState(targetPos);\n            BlockEntity te = view.getContext().getWorld().getBlockEntity(targetPos);\n\n            if (renderBlockState.getRenderShape() == RenderShape.MODEL) {\n                BakedModel model = dispatcher.getBlockModel(renderBlockState);\n//                dispatcher.getBlockModelRenderer().renderModelFlat()\n//                        .renderModelFlat(getWorld(), model, renderBlockState, target.getPos(), bufferBuilder, false,\n//                        rand, 0L, te != null ? te.getModelData() : EmptyModelData.INSTANCE);\n            }\n\n            if (te != null) {\n                try {\n                    BlockEntityRenderer<BlockEntity> renderer = Minecraft.getInstance().getBlockEntityRenderDispatcher().getRenderer(te);\n                    if (renderer != null) {\n//                        if (te.hasFastRenderer())\n//                            renderer.renderTileEntityFast(te, targetPos.getX(), targetPos.getY(), targetPos.getZ(), partialTicks, - 1, bufferBuilder);\n//                        else\n//                            renderer.render(te, targetPos.getX(), targetPos.getY(), targetPos.getZ(), partialTicks, - 1);\n                    }\n                    //remember vanilla Tiles rebinding the TextureAtlas\n                    RenderSystem.setShaderTexture(0, InventoryMenu.BLOCK_ATLAS);\n                } catch (Exception e) {\n                    BuildingGadgets.LOG.error(\"Error rendering TileEntity\", e);\n                }\n            }\n        }\n\n        bufferBuilder.end();\n\n//        if (bufferBuilder.getVertexCount() > 0) {\n//            VertexFormat vertexformat = bufferBuilder.getVertexFormat();\n//            int i = vertexformat.getSize();\n//            ByteBuffer bytebuffer = bufferBuilder.getByteBuffer();\n//            List<VertexFormatElement> list = vertexformat.getElements();\n//\n//            for (int j = 0; j < list.size(); ++ j) {\n//                VertexFormatElement vertexformatelement = list.get(j);\n//                bytebuffer.position(vertexformat.getOffset(j));\n//                vertexformatelement.getUsage().preDraw(vertexformat, j, i, bytebuffer);\n//            }\n//\n//            GlStateManager.drawArrays(bufferBuilder.getDrawMode(), 0, bufferBuilder.getVertexCount());\n//            int i1 = 0;\n//\n//            for (int j1 = list.size(); i1 < j1; ++ i1) {\n//                VertexFormatElement vertexformatelement1 = list.get(i1);\n//                vertexformatelement1.getUsage().postDraw(vertexformat, i1, i, bytebuffer);\n//            }\n//        }\n    }\n\n    private void renderRequirement(PoseStack matrices, int mouseX, int mouseY) {\n        MaterialList requirements = this.template.getHeaderAndForceMaterials(BuildContext.builder().build(getWorld())).getRequiredItems();\n        if (requirements == null)\n            return;\n\n        Lighting.setupForFlatItems();\n\n        matrices.pushPose();\n        matrices.translate(leftPos - 30, topPos - 5, 200);\n        matrices.scale(.8f, .8f, .8f);\n\n        String title = \"Requirements\"; // Todo lang;\n        drawString(matrices, getMinecraft().font, title, 5 - (font.width(title)), 0, Color.WHITE.getRGB());\n\n        // The things you have to do to get anything from this system is just stupid.\n        MatchResult list = InventoryHelper.CREATIVE_INDEX.tryMatch(requirements);\n        ImmutableMultiset<IUniqueObject<?>> foundItems = list.getFoundItems();\n\n        // Reverse sorted list of items required.\n        List<Multiset.Entry<IUniqueObject<?>>> sortedEntries = ImmutableList.sortedCopyOf(Comparator\n                .<Multiset.Entry<IUniqueObject<?>>, Integer>comparing(Multiset.Entry::getCount)\n                .reversed(), list.getChosenOption().entrySet());\n\n        int index = 0, column = 0;\n        for (Multiset.Entry<IUniqueObject<?>> e : sortedEntries) {\n            ItemStack stack = e.getElement().createStack();\n            int x = (-20 - (column * 25)), y = (20 + (index * 25));\n\n            itemRenderer.renderAndDecorateItem(matrices, stack, x + 4, y + 4);\n            itemRenderer.renderGuiItemDecorations(matrices, Minecraft.getInstance().font, stack, x + 4, y + 4, GadgetUtils.withSuffix(foundItems.count(e.getElement())));\n\n            int space = (int) (25 - (.2f * 25));\n            int zoneX = ((leftPos - 32) + (-15 - (column * space))), zoneY = (topPos - 9) + (20 + (index * space));\n\n            if (mouseX > zoneX && mouseX < (zoneX + space) && mouseY > zoneY && mouseY < (zoneY + space)) {\n                renderTooltip(matrices, Lists.transform(stack.getTooltipLines(this.getMinecraft().player, TooltipFlag.Default.NORMAL), Component::getVisualOrderText), x + 15, y + 25);\n            }\n\n            index++;\n            if (index % 8 == 0) {\n                column++;\n                index = 0;\n            }\n        }\n\n        Lighting.setupFor3DItems();\n        matrices.popPose();\n    }\n\n    private void pasteTemplateToStack(Level world, ItemStack stack, Template newTemplate, boolean replaced) {\n        world.getCapability(CapabilityTemplate.TEMPLATE_PROVIDER_CAPABILITY).ifPresent(provider ->\n                pasteTemplateToStack(provider, stack, newTemplate, replaced && world.isClientSide()));\n    }\n\n    private void pasteTemplateToStack(ITemplateProvider provider, ItemStack stack, Template newTemplate, boolean replaced) {\n        stack.getCapability(CapabilityTemplate.TEMPLATE_KEY_CAPABILITY).ifPresent(key -> {\n            provider.setTemplate(key, newTemplate);\n            if (replaced)\n                PacketHandler.sendToServer(new PacketTemplateManagerTemplateCreated(provider.getId(key), te.getBlockPos()));\n            else\n                provider.requestRemoteUpdate(key);\n        });\n    }\n\n    private boolean replaceStack() {\n        ItemStack stack = container.getSlot(1).getItem();\n        if (stack.isEmpty())\n            return false;\n\n        if (stack.getCapability(CapabilityTemplate.TEMPLATE_KEY_CAPABILITY).isPresent())\n            return false;\n\n        else if (stack.is(TemplateManagerTileEntity.TEMPLATE_CONVERTIBLES)) {\n            container.setItem(1, container.getStateId(), new ItemStack(OurItems.TEMPLATE_ITEM.get()));\n            return true;\n        }\n\n        return false;\n    }\n\n    private void rename(ItemStack stack) {\n        if (nameField.getValue().isEmpty())\n            return;\n\n        stack.getCapability(CapabilityTemplate.TEMPLATE_KEY_CAPABILITY).ifPresent(key -> templateProvider.ifPresent(provider -> {\n            Template template = provider.getTemplateForKey(key);\n            template = template.withName(nameField.getValue());\n            provider.setTemplate(key, template);\n            provider.requestRemoteUpdate(key);\n        }));\n    }\n\n    private void renderPanel() {\n//        double scale = getMinecraft().getWindow().getGuiScale();\n//\n//        BlockPos startPos = template.getHeader().getBoundingBox().getMin();\n//        BlockPos endPos = template.getHeader().getBoundingBox().getMax();\n//\n//        double lengthX = Math.abs(startPos.getX() - endPos.getX());\n//        double lengthY = Math.abs(startPos.getY() - endPos.getY());\n//        double lengthZ = Math.abs(startPos.getZ() - endPos.getZ());\n//\n//        final double maxW = 6 * 16;\n//        final double maxH = 11 * 16;\n//\n//        double overW = Math.max(lengthX * 16 - maxW, lengthZ * 16 - maxW);\n//        double overH = lengthY * 16 - maxH;\n//\n//        double sc = 1;\n//        double zoomScale = 1;\n//\n//        if (overW > 0 && overW >= overH) {\n//            sc = maxW / (overW + maxW);\n//            zoomScale = overW / 40;\n//        } else if (overH > 0 && overH >= overW) {\n//            sc = maxH / (overH + maxH);\n//            zoomScale = overH / 40;\n//        }\n//\n//        RenderSystem.pushMatrix();\n//        RenderSystem.matrixMode(GL11.GL_PROJECTION);\n//        RenderSystem.pushMatrix();\n//        RenderSystem.loadIdentity();\n//\n//        RenderSystem.multMatrix(Matrix4f.perspective(60, (float) panel.getWidth() / panel.getHeight(), 0.01F, 4000));\n//        RenderSystem.matrixMode(GL11.GL_MODELVIEW);\n//        RenderSystem.viewport((int) Math.round((leftPos + panel.getX()) * scale),\n//                (int) Math.round(getMinecraft().getWindow().getHeight() - (topPos + panel.getY() + panel.getHeight()) * scale),\n//                (int) Math.round(panel.getWidth() * scale),\n//                (int) Math.round(panel.getHeight() * scale));\n//\n//        RenderSystem.clear(GL11.GL_DEPTH_BUFFER_BIT, true);\n//\n//        sc = (293 * sc) + zoom / zoomScale;\n//        RenderSystem.scaled(sc, sc, sc);\n//        int moveX = startPos.getX() - endPos.getX();\n//\n//        RenderSystem.rotatef(30, 0, 1, 0);\n//        if (startPos.getX() >= endPos.getX())\n//            moveX--;\n//\n//        RenderSystem.translated((moveX) / 1.75, -Math.abs(startPos.getY() - endPos.getY()) / 1.75, 0);\n//        RenderSystem.translated(panX, -panY, 0);\n//        RenderSystem.translated(((startPos.getX() - endPos.getX()) / 2f) * -1, ((startPos.getY() - endPos.getY()) / 2f) * -1, ((startPos.getZ() - endPos.getZ()) / 2f) * -1);\n//        RenderSystem.rotatef(-rotX, 1, 0, 0);\n//        RenderSystem.rotatef(rotY, 0, 1, 0);\n//        RenderSystem.translated(((startPos.getX() - endPos.getX()) / 2f), ((startPos.getY() - endPos.getY()) / 2f), ((startPos.getZ() - endPos.getZ()) / 2f));\n//\n//        getMinecraft().getTextureManager().bind(InventoryMenu.BLOCK_ATLAS);\n//\n////        RenderSystem.callList(displayList);\n//\n//        RenderSystem.popMatrix();\n//        RenderSystem.matrixMode(GL11.GL_PROJECTION);\n//        RenderSystem.popMatrix();\n//        RenderSystem.matrixMode(GL11.GL_MODELVIEW);\n//        RenderSystem.viewport(0, 0, getMinecraft().getWindow().getWidth(), getMinecraft().getWindow().getHeight());\n    }\n\n    private void resetViewport() {\n        rotX = 0;\n        rotY = 0;\n        zoom = 1;\n        momentumX = 0;\n        momentumY = 0;\n        panX = 0;\n        panY = 0;\n    }\n\n    @Override\n    public boolean mouseClicked(double mouseX, double mouseY, int mouseButton) {\n        if (panel.contains((int) mouseX - leftPos, (int) mouseY - topPos)) {\n            clickButton = mouseButton;\n            panelClicked = true;\n            clickX = (int) getMinecraft().mouseHandler.xpos();\n            clickY = (int) getMinecraft().mouseHandler.ypos();\n        }\n\n        return super.mouseClicked(mouseX, mouseY, mouseButton);\n    }\n\n    @Override\n    public boolean mouseReleased(double mouseX, double mouseY, int state) {\n        panelClicked = false;\n        initRotX = rotX;\n        initRotY = rotY;\n        initPanX = panX;\n        initPanY = panY;\n        initZoom = zoom;\n\n        return super.mouseReleased(mouseX, mouseY, state);\n    }\n\n    @Override\n    public boolean keyPressed(int p_keyPressed_1_, int p_keyPressed_2_, int p_keyPressed_3_) {\n        if (p_keyPressed_1_ == 256) {\n            this.onClose();\n            return true;\n        }\n\n        return this.nameField.isFocused() ? this.nameField.keyPressed(p_keyPressed_1_, p_keyPressed_2_, p_keyPressed_3_) : super.keyPressed(p_keyPressed_1_, p_keyPressed_2_, p_keyPressed_3_);\n    }\n\n    @Override\n    protected void renderLabels(PoseStack matrices, int mouseX, int mouseY) {\n        if (panelClicked) {\n            if (clickButton == 0) {\n                float prevRotX = rotX;\n                float prevRotY = rotY;\n                rotX = initRotX - ((int) getMinecraft().mouseHandler.ypos() - clickY);\n                rotY = initRotY + ((int) getMinecraft().mouseHandler.xpos() - clickX);\n                momentumX = rotX - prevRotX;\n                momentumY = rotY - prevRotY;\n            } else if (clickButton == 1) {\n                panX = initPanX + ((int) getMinecraft().mouseHandler.xpos() - clickX) / 8f;\n                panY = initPanY + ((int) getMinecraft().mouseHandler.ypos() - clickY) / 8f;\n            }\n        }\n\n        rotX += momentumX;\n        rotY += momentumY;\n        float momentumDampening = 0.98f;\n        momentumX *= momentumDampening;\n        momentumY *= momentumDampening;\n\n        if (!nameField.isFocused() && nameField.getValue().isEmpty())\n            getMinecraft().font.draw(matrices, GuiTranslation.TEMPLATE_PLACEHOLDER.format(), nameField.getX() - leftPos + 4, (nameField.getY() + 2) - topPos, -10197916);\n\n        if (buttonSave.isHoveredOrFocused() || buttonLoad.isHoveredOrFocused() || buttonPaste.isHoveredOrFocused())\n            drawSlotOverlay(matrices, buttonLoad.isHoveredOrFocused() ? container.getSlot(0) : container.getSlot(1));\n    }\n\n    private void drawSlotOverlay(PoseStack matrices, Slot slot) {\n        matrices.pushPose();\n        matrices.translate(0, 0, 1000);\n        fill(matrices, slot.x, slot.y, slot.x + 16, slot.y + 16, -1660903937);\n        matrices.popPose();\n    }\n\n    @Override\n    public boolean mouseScrolled(double mouseX, double mouseY, double scrollDelta) {\n        zoom = initZoom + ((float) scrollDelta * 20);\n        if (zoom < -200) zoom = -200;\n        if (zoom > 5000) zoom = 5000;\n\n        return super.mouseScrolled(mouseX, mouseY, scrollDelta);\n    }\n\n    @Override\n    protected void containerTick() {\n        super.containerTick();\n\n        nameField.tick();\n        if (!panelClicked) {\n            initRotX = rotX;\n            initRotY = rotY;\n            initZoom = zoom;\n            initPanX = panX;\n            initPanY = panY;\n        }\n    }\n\n//    @Override\n//    public void tick() {\n//        super.tick();\n//        nameField.tick();\n//        if (! panelClicked) {\n//            initRotX = rotX;\n//            initRotY = rotY;\n//            initZoom = zoom;\n//            initPanX = panX;\n//            initPanY = panY;\n//        }\n//    }\n\n    private Level getWorld() {\n        return getMinecraft().level;\n    }\n\n    @Override\n    public Minecraft getMinecraft() {\n        return Minecraft.getInstance();\n    }\n\n    // Events\n    // we need to ensure that the Template we want to look at is recent, before we take any further action\n    private void runAfterUpdate(int slot, Runnable runnable) {\n        container.getSlot(slot).getItem().getCapability(CapabilityTemplate.TEMPLATE_KEY_CAPABILITY).ifPresent(key -> templateProvider.ifPresent(provider -> {\n            provider.registerUpdateListener(new IUpdateListener() {\n                @Override\n                public void onTemplateUpdate(ITemplateProvider provider, ITemplateKey updateKey, Template template) {\n                    if (provider.getId(updateKey).equals(provider.getId(key))) {\n                        runnable.run();\n                        provider.removeUpdateListener(this);\n                    }\n                }\n            });\n            provider.requestUpdate(key);\n        }));\n    }\n\n    private void onSave() {\n        boolean replaced = replaceStack();\n        ItemStack left = container.getSlot(0).getItem();\n        ItemStack right = container.getSlot(1).getItem();\n        if (left.isEmpty()) {\n            rename(right);\n            return;\n        }\n\n        runAfterUpdate(0, () -> { //we are copying form 0 to 1 => slot 0 needs to be the recent one\n            templateProvider.ifPresent(provider -> {\n                left.getCapability(CapabilityTemplate.TEMPLATE_KEY_CAPABILITY).ifPresent(key -> {\n                    Template templateToSave = provider.getTemplateForKey(key).withName(nameField.getValue());\n                    pasteTemplateToStack(provider, right, templateToSave, replaced);\n                });\n            });\n        });\n    }\n\n    private void onLoad() {\n        boolean replaced = replaceStack();\n        ItemStack left = container.getSlot(0).getItem();\n        ItemStack right = container.getSlot(1).getItem();\n        if (left.isEmpty()) {\n            rename(right);\n            return;\n        }\n\n        runAfterUpdate(1, () -> { //we are copying form 1 to 0 => slot 1 needs to be the recent one\n            templateProvider.ifPresent(provider -> {\n                right.getCapability(CapabilityTemplate.TEMPLATE_KEY_CAPABILITY).ifPresent(key -> {\n                    Template templateToSave = provider.getTemplateForKey(key);\n                    pasteTemplateToStack(provider, left, templateToSave, replaced);\n                });\n            });\n        });\n    }\n\n    private void onCopy() {\n        runAfterUpdate(0, () -> { //we are copying from slot 1 => slot 1 needs to be updated\n            ItemStack stack = container.getSlot(0).getItem();\n            stack.getCapability(CapabilityTemplate.TEMPLATE_KEY_CAPABILITY).ifPresent(key -> {\n                templateProvider.ifPresent(provider -> {\n                    Player player = getMinecraft().player;\n                    assert player != null;\n\n                    BuildContext buildContext = BuildContext.builder()\n                            .player(player)\n                            .stack(stack)\n                            .build(getWorld());\n                    try {\n                        Template template = provider.getTemplateForKey(key);\n                        if (!nameField.getValue().isEmpty())\n                            template = template.withName(nameField.getValue());\n                        String json = TemplateIO.writeTemplateJson(template, buildContext);\n                        getMinecraft().keyboardHandler.setClipboard(json);\n                        player.displayClientMessage(MessageTranslation.CLIPBOARD_COPY_SUCCESS.componentTranslation().setStyle(Styles.DK_GREEN), false);\n                    } catch (DataCannotBeWrittenException e) {\n                        BuildingGadgets.LOG.error(\"Failed to write Template.\", e);\n                        player.displayClientMessage(MessageTranslation.CLIPBOARD_COPY_ERROR_TEMPLATE.componentTranslation().setStyle(Styles.RED), false);\n                    } catch (Exception e) {\n                        BuildingGadgets.LOG.error(\"Failed to copy Template to clipboard.\", e);\n                        player.displayClientMessage(MessageTranslation.CLIPBOARD_COPY_ERROR.componentTranslation().setStyle(Styles.RED), false);\n                    }\n                });\n            });\n        });\n    }\n\n    private void onPaste() {\n        assert getMinecraft().player != null;\n\n        String CBString = getMinecraft().keyboardHandler.getClipboard();\n        if (GadgetUtils.mightBeLink(CBString)) {\n            getMinecraft().player.displayClientMessage(MessageTranslation.PASTE_FAILED_LINK_COPIED.componentTranslation().setStyle(Styles.RED), false);\n            return;\n        }\n\n        // Attempt to parse into nbt first to check for old 1.12 pastes\n        try {\n            CompoundTag tagFromJson = TagParser.parseTag(CBString);\n            if (!tagFromJson.contains(\"header\")) {\n                BuildingGadgets.LOG.error(\"Attempted to use a 1.12 compound on a newer MC version\");\n                getMinecraft().player.displayClientMessage(MessageTranslation.PASTE_FAILED_WRONG_MC_VERSION\n                        .componentTranslation(\"(1.12.x)\", \"1.14.x\", SharedConstants.VERSION_STRING).setStyle(Styles.RED), false);\n                return;\n\n            }\n        } catch (CommandSyntaxException ignored) {\n        }\n\n        // todo: this needs to be put onto some kind of readTemplateFromJson(input stream).onError(e -> error)\n        try {\n            Template template = TemplateIO.readTemplateFromJson(CBString);\n            Template readTemplate = template.clearMaterials();\n            if (!nameField.getValue().isEmpty())\n                readTemplate = readTemplate.withName(nameField.getValue());\n            boolean replaced = replaceStack();\n            ItemStack stack = container.getSlot(1).getItem();\n            pasteTemplateToStack(getWorld(), stack, readTemplate, replaced);\n            getMinecraft().player.displayClientMessage(MessageTranslation.PASTE_SUCCESS.componentTranslation().setStyle(Styles.DK_GREEN), false);\n        } catch (CorruptJsonException e) {\n            BuildingGadgets.LOG.error(\"Failed to parse json syntax.\", e);\n            getMinecraft().player.displayClientMessage(MessageTranslation.PASTE_FAILED_CORRUPT_JSON\n                    .componentTranslation().setStyle(Styles.RED), false);\n        } catch (IllegalMinecraftVersionException e) {\n            BuildingGadgets.LOG.error(\"Attempted to parse Template for Minecraft version {} but expected between {} and {}.\",\n                    e.getMinecraftVersion(), TemplateHeader.LOWEST_MC_VERSION, TemplateHeader.HIGHEST_MC_VERSION, e);\n            getMinecraft().player.displayClientMessage(MessageTranslation.PASTE_FAILED_WRONG_MC_VERSION\n                    .componentTranslation(e.getMinecraftVersion(), TemplateHeader.LOWEST_MC_VERSION, TemplateHeader.HIGHEST_MC_VERSION).setStyle(Styles.RED), false);\n        } catch (UnknownTemplateVersionException e) {\n            BuildingGadgets.LOG.error(\"Attempted to parse Template version {} but newest is {}.\",\n                    e.getTemplateVersion(), TemplateHeader.VERSION, e);\n            getMinecraft().player.displayClientMessage(MessageTranslation.PASTE_FAILED_TOO_RECENT_VERSION\n                    .componentTranslation(e.getTemplateVersion(), TemplateHeader.VERSION).setStyle(Styles.RED), false);\n        } catch (JsonParseException e) {\n            BuildingGadgets.LOG.error(\"Failed to parse Template json.\", e);\n            getMinecraft().player.displayClientMessage(MessageTranslation.PASTE_FAILED_INVALID_JSON\n                    .componentTranslation().setStyle(Styles.RED), false);\n        } catch (TemplateReadException e) {\n            BuildingGadgets.LOG.error(\"Failed to read Template body.\", e);\n            getMinecraft().player.displayClientMessage(MessageTranslation.PASTE_FAILED_CORRUPT_BODY\n                    .componentTranslation().setStyle(Styles.RED), false);\n        } catch (Exception e) {\n            BuildingGadgets.LOG.error(\"Failed to paste Template.\", e);\n            getMinecraft().player.displayClientMessage(MessageTranslation.PASTE_FAILED\n                    .componentTranslation().setStyle(Styles.RED), false);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/client/screen/widgets/DireButton.java",
    "content": "package com.direwolf20.buildinggadgets.client.screen.widgets;\n\nimport com.mojang.blaze3d.platform.GlStateManager;\nimport com.mojang.blaze3d.systems.RenderSystem;\nimport com.mojang.blaze3d.vertex.PoseStack;\nimport net.minecraft.client.Minecraft;\nimport net.minecraft.client.gui.Font;\nimport net.minecraft.client.gui.components.Button;\nimport net.minecraft.network.chat.Component;\n\npublic class DireButton extends Button {\n\n    public DireButton(int x, int y, int widthIn, int heightIn, Component buttonText, OnPress action) {\n        super(builder(buttonText, action)\n                .size(widthIn, heightIn)\n                .pos(x, y));\n    }\n\n    @Override\n    public void render(PoseStack matrices, int mouseX, int mouseY, float partialTicks) {\n        if (this.visible) {\n            Font fontrenderer = Minecraft.getInstance().font;\n            RenderSystem.setShaderTexture(0, WIDGETS_LOCATION);\n            RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);\n            this.isHovered = isMouseOver(mouseX, mouseY);\n            RenderSystem.enableBlend();\n            RenderSystem.blendFuncSeparate(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO);\n            RenderSystem.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA);\n            this.blit(matrices, this.getX(), this.getY(), 0, 46, this.width / 2, this.height);\n            this.blit(matrices, this.getX() + this.width / 2, this.getY(), 200 - this.width / 2, 46, this.width / 2, this.height);\n\n\n            int bottomToDraw = 2;\n            this.blit(matrices, this.getX(), this.getY() + this.height - bottomToDraw, 0, 66 - bottomToDraw, this.width / 2, bottomToDraw);\n            this.blit(matrices, this.getX() + this.width / 2, this.getY() + this.height - bottomToDraw, 200 - this.width / 2, 66 - bottomToDraw, this.width / 2, bottomToDraw);\n\n            int j = 14737632;\n\n            if (this.packedFGColor != 0) {\n                j = this.packedFGColor;\n            } else if (! this.active) {\n                j = 10526880;\n            } else if (this.isHovered) {\n                j = 16777120;\n            }\n\n            this.drawCenteredString(matrices, fontrenderer, this.getMessage().getString(), this.getX() + this.width / 2, this.getY() + (this.height - 7) / 2, j);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/client/screen/widgets/EntryList.java",
    "content": "package com.direwolf20.buildinggadgets.client.screen.widgets;\n\nimport com.mojang.blaze3d.systems.RenderSystem;\nimport com.mojang.blaze3d.vertex.*;\nimport net.minecraft.client.Minecraft;\nimport net.minecraft.client.gui.components.ObjectSelectionList;\nimport net.minecraft.client.gui.components.ObjectSelectionList.Entry;\nimport net.minecraft.util.Mth;\nimport org.lwjgl.opengl.GL11;\n\nimport static org.lwjgl.opengl.GL11.*;\n\npublic class EntryList<E extends Entry<E>> extends ObjectSelectionList<E> {\n\n    public static final int SCROLL_BAR_WIDTH = 6;\n\n    public EntryList(int left, int top, int width, int height, int slotHeight) {\n        super(Minecraft.getInstance(), width, height, top, top + height, slotHeight);\n        // Set left x and right x, somehow MCP gave it a weird name\n        this.setLeftPos(left);\n    }\n\n    @Override\n    public void render(PoseStack matrices, int mouseX, int mouseY, float partialTicks) {\n        glEnable(GL_SCISSOR_TEST);\n        double guiScaleFactor = Minecraft.getInstance().getWindow().getGuiScale();\n\n        GL11.glEnable(GL11.GL_SCISSOR_TEST);\n        GL11.glScissor((int)(getLeft()  * guiScaleFactor),\n                (int)(Minecraft.getInstance().getWindow().getHeight() - (getBottom() * guiScaleFactor)),\n                (int)(width * guiScaleFactor),\n                (int)(height * guiScaleFactor));\n\n        renderParts(matrices, mouseX, mouseY, partialTicks);\n        glDisable(GL_SCISSOR_TEST);\n    }\n\n    // Copied and modified from AbstractLists#render(int, int, float)\n    private void renderParts(PoseStack matrices, int mouseX, int mouseY, float partialTicks) {\n        renderBackground(matrices);\n//        RenderSystem.disableLighting();\n//        RenderSystem.disableFog();\n        Tesselator tessellator = Tesselator.getInstance();\n        BufferBuilder bufferbuilder = tessellator.getBuilder();\n\n        renderContentBackground(matrices, tessellator, bufferbuilder);\n\n        int k = getRowLeft();\n        int l = getTop() + 4 - (int) getScrollAmount();\n        renderHeader(matrices, k, l);\n\n        renderList(matrices, k, l, partialTicks);\n        RenderSystem.disableDepthTest();\n\n        int j1 = getMaxScroll();\n        if (j1 > 0) {\n            int k1 = (int) ((float) ((getBottom() - getTop()) * (getBottom() - getTop())) / (float) getMaxPosition());\n            k1 = Mth.clamp(k1, 32, getBottom() - getTop() - 8);\n            int l1 = (int) getScrollAmount() * (getBottom() - getTop() - k1) / j1 + getTop();\n            if (l1 < getTop()) {\n                l1 = getTop();\n            }\n            int x1 = getScrollbarPosition();\n            int x2 = x1 + 6;\n\n            bufferbuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR);\n            bufferbuilder.vertex(x1, getBottom(), 0.0D).color(0, 0, 0, 255).endVertex();\n            bufferbuilder.vertex(x2, getBottom(), 0.0D).color(0, 0, 0, 255).endVertex();\n            bufferbuilder.vertex(x2, getTop(), 0.0D).color(0, 0, 0, 255).endVertex();\n            bufferbuilder.vertex(x1, getTop(), 0.0D).color(0, 0, 0, 255).endVertex();\n\n            bufferbuilder.vertex(x1, (l1 + k1), 0.0D).color(128, 128, 128, 255).endVertex();\n            bufferbuilder.vertex(x2, (l1 + k1), 0.0D).color(128, 128, 128, 255).endVertex();\n            bufferbuilder.vertex(x2, l1, 0.0D).color(128, 128, 128, 255).endVertex();\n            bufferbuilder.vertex(x1, l1, 0.0D).color(128, 128, 128, 255).endVertex();\n\n            bufferbuilder.vertex(x1, (l1 + k1 - 1), 0.0D).color(192, 192, 192, 255).endVertex();\n            bufferbuilder.vertex((x2 - 1), (l1 + k1 - 1), 0.0D).color(192, 192, 192, 255).endVertex();\n            bufferbuilder.vertex((x2 - 1), l1, 0.0D).color(192, 192, 192, 255).endVertex();\n            bufferbuilder.vertex(x1, l1, 0.0D).color(192, 192, 192, 255).endVertex();\n            tessellator.end();\n        }\n\n        renderDecorations(matrices, mouseX, mouseX);\n        RenderSystem.disableBlend();\n    }\n\n    protected void renderContentBackground(PoseStack matrices, Tesselator tessellator, BufferBuilder bufferbuilder) {\n        fillGradient(matrices, getLeft(), getTop(), getRight(), getBottom(), 0xC0101010, 0xD0101010);\n    }\n\n    @Override\n    protected void renderBackground(PoseStack p_230433_1_) {\n        super.renderBackground(p_230433_1_);\n    }\n\n    @Override\n    public boolean mouseClicked(double x, double y, int button) {\n        setDragging(true);\n        super.mouseClicked(x, y, button);\n        return isMouseOver(x, y);\n    }\n\n    @Override\n    public boolean mouseReleased(double x, double y, int button) {\n        setDragging(false);\n        return super.mouseReleased(x, y, button);\n    }\n\n    @Override\n    public boolean mouseDragged(double x, double y, int button, double dx, double dy) {\n        if (super.mouseDragged(x, y, button, dx, dy))\n            return true;\n\n        // Dragging elements in panel\n        if (isMouseOver(x, y)) {\n            setScrollAmount(getScrollAmount() - dy);\n        }\n        return true;\n    }\n\n    // Copied from AbstractList#getMaxScroll because it is private\n    public final int getMaxScroll() {\n        return Math.max(0, this.getMaxPosition() - (this.getBottom() - this.getTop() - 4));\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/client/screen/widgets/GuiIconActionable.java",
    "content": "package com.direwolf20.buildinggadgets.client.screen.widgets;\n\nimport com.direwolf20.buildinggadgets.client.OurSounds;\nimport com.direwolf20.buildinggadgets.common.util.ref.Reference;\nimport com.mojang.blaze3d.platform.GlStateManager;\nimport com.mojang.blaze3d.systems.RenderSystem;\nimport com.mojang.blaze3d.vertex.PoseStack;\nimport net.minecraft.client.Minecraft;\nimport net.minecraft.client.gui.components.Button;\nimport net.minecraft.client.resources.sounds.SimpleSoundInstance;\nimport net.minecraft.client.sounds.SoundManager;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.resources.ResourceLocation;\n\nimport java.awt.*;\nimport java.util.function.Predicate;\n\n/**\n * A one stop shop for all your icon gui related needs. We support colors,\n * icons, selected and deselected states, sound and loads more. Come on\n * down!\n */\npublic class GuiIconActionable extends Button {\n    private Predicate<Boolean> action;\n    private boolean selected;\n    private boolean isSelectable;\n\n    private final Color selectedColor = new Color(0, 255, 0, 50);\n    private final Color deselectedColor = new Color(255, 255, 255, 50);\n    private Color activeColor;\n\n    private final ResourceLocation selectedTexture;\n    private final ResourceLocation deselectedTexture;\n\n    public GuiIconActionable(int x, int y, String texture, Component message, boolean isSelectable, Predicate<Boolean> action) {\n        super(builder(message, (button) -> {}).pos(x, y).size(25, 25));\n        this.activeColor = deselectedColor;\n        this.isSelectable = isSelectable;\n        this.action = action;\n\n        this.setSelected(action.test(false));\n\n        // Set the selected and deselected textures.\n        String assetLocation = \"textures/gui/setting/%s.png\";\n\n        this.deselectedTexture = new ResourceLocation(Reference.MODID, String.format(assetLocation, texture));\n        this.selectedTexture = !isSelectable ? this.deselectedTexture : new ResourceLocation(Reference.MODID, String.format(assetLocation, texture + \"_selected\"));\n    }\n\n    /**\n     * If yo do not need to be able to select / toggle something then use this constructor as\n     * you'll hit missing texture issues if you don't have an active (_selected) texture.\n     */\n    public GuiIconActionable(int x, int y, String texture, Component message, Predicate<Boolean> action) {\n        this(x, y, texture, message, false, action);\n    }\n\n    public void setFaded(boolean faded) {\n        alpha = faded ? .6f : 1f;\n    }\n\n    /**\n     * This should be used when ever-changing select.\n     */\n    public void setSelected(boolean selected) {\n        this.selected = selected;\n        this.activeColor = selected ? selectedColor : deselectedColor;\n    }\n\n    @Override\n    public void playDownSound(SoundManager soundHandler) {\n        soundHandler.play(SimpleSoundInstance.forUI(OurSounds.BEEP.get(), selected ? .6F : 1F));\n    }\n\n    @Override\n    public void onClick(double mouseX, double mouseY) {\n        super.onClick(mouseX, mouseY);\n        this.action.test(true);\n\n        if (!this.isSelectable)\n            return;\n\n        this.setSelected(!this.selected);\n    }\n\n    @Override\n    public void render(PoseStack matrices, int mouseX, int mouseY, float partialTick) {\n        if (!visible)\n            return;\n\n        RenderSystem.enableBlend();\n        RenderSystem.blendFuncSeparate(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO);\n        RenderSystem.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA);\n\n        fill(matrices, this.getX(), this.getY(), this.getX() + this.width, this.getY() + this.height, activeColor.getRGB());\n\n//        RenderSystem.setShaderColor(activeColor.getRGB().getRed() / 255f, activeColor.getGreen() / 255f, activeColor.getBlue() / 255f, .15f);\n//        fill(matrices, this.x, this.y, this.x + this.width, this.y + this.height, -1873784752);\n\n        RenderSystem.setShaderTexture(0, selected ? selectedTexture : deselectedTexture);\n        RenderSystem.setShaderColor(activeColor.getRed() / 255f, activeColor.getGreen() / 255f, activeColor.getBlue() / 255f, alpha);\n\n        blit(matrices, this.getX(), this.getY(), 0, 0, this.width, this.height, this.width, this.height);\n        RenderSystem.setShaderColor(1, 1, 1, 1);\n        RenderSystem.disableBlend();\n\n        if (mouseX >= getX() && mouseY >= getY() && mouseX < getX() + width && mouseY < getY() + height)\n            drawString(matrices, Minecraft.getInstance().font, this.getMessage().getString(), mouseX > (Minecraft.getInstance().getWindow().getGuiScaledWidth() / 2) ? mouseX + 2 : mouseX - Minecraft.getInstance().font.width(getMessage().getString()), mouseY - 10, activeColor.getRGB() | 0xFF000000);\n\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/client/screen/widgets/GuiIncrementer.java",
    "content": "package com.direwolf20.buildinggadgets.client.screen.widgets;\n\nimport com.mojang.blaze3d.vertex.PoseStack;\nimport net.minecraft.client.Minecraft;\nimport net.minecraft.client.gui.components.AbstractWidget;\nimport net.minecraft.client.gui.narration.NarrationElementOutput;\nimport net.minecraft.client.gui.screens.Screen;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.util.Mth;\n\nimport javax.annotation.Nullable;\n\npublic class GuiIncrementer extends AbstractWidget {\n    // this is the width of all components in a line\n    public static final int WIDTH = 64;\n\n    private int x;\n    private int y;\n    private int min;\n    private int max;\n\n    private int value;\n    private IIncrementerChanged onChange;\n\n    private DireButton minusButton;\n    private GuiTextFieldBase field;\n    private DireButton plusButton;\n\n    public GuiIncrementer(int x, int y, int min, int max, @Nullable IIncrementerChanged onChange) {\n        super(x, y, WIDTH, 20, Component.empty());\n\n        this.x = x;\n        this.y = y;\n        this.min = min;\n        this.max = max;\n        this.value = 0;\n        this.onChange = onChange;\n\n        this.minusButton = new DireButton(this.x, this.y - 1, 12, 17, Component.literal(\"-\"), (button) -> this.updateValue(true));\n        this.field = new GuiTextFieldBase(Minecraft.getInstance().font, x + 13, y, 40).setDefaultInt(this.value).restrictToNumeric();\n        this.plusButton = new DireButton(this.x + 40 + 14, this.y - 1, 12, 17, Component.literal(\"+\"), (button) -> this.updateValue(false));\n\n        this.field.setValue(String.valueOf(this.value));\n    }\n\n    public GuiIncrementer(int x, int y) {\n        this(x, y, Integer.MIN_VALUE, Integer.MAX_VALUE, null);\n    }\n\n    public int getValue() {\n        return this.value;\n    }\n\n    private void updateValue(boolean isMinus) {\n        int modifier = 1;\n        if (Screen.hasShiftDown())\n            modifier *= 10;\n\n        int value = isMinus ? this.value - modifier : this.value + modifier;\n        this.setValue(value);\n    }\n\n    public void setValue(int value) {\n        // We don't want to fire events for no reason\n        if (value == this.value)\n            return;\n\n        this.value = Mth.clamp(value, this.min, this.max);\n        this.field.setValue(String.valueOf(this.value));\n\n        if (this.onChange != null)\n            this.onChange.onChange(value);\n    }\n\n    @Override\n    public void renderWidget(PoseStack matrices, int mouseX, int mouseY, float partialTick) {\n        this.plusButton.render(matrices, mouseX, mouseY, partialTick);\n        this.minusButton.render(matrices, mouseX, mouseY, partialTick);\n        this.field.render(matrices, mouseX, mouseY, partialTick);\n    }\n\n    @Override\n    public boolean mouseClicked(double p_mouseClicked_1_, double p_mouseClicked_3_, int p_mouseClicked_5_) {\n        this.field.mouseClicked(p_mouseClicked_1_, p_mouseClicked_3_, p_mouseClicked_5_);\n        this.plusButton.mouseClicked(p_mouseClicked_1_, p_mouseClicked_3_, p_mouseClicked_5_);\n        this.minusButton.mouseClicked(p_mouseClicked_1_, p_mouseClicked_3_, p_mouseClicked_5_);\n\n        return false;\n    }\n\n    @Override\n    public boolean keyPressed(int p_keyPressed_1_, int p_keyPressed_2_, int p_keyPressed_3_) {\n        if (!this.field.isFocused())\n            return false;\n\n        this.field.keyPressed(p_keyPressed_1_, p_keyPressed_2_, p_keyPressed_3_);\n        return true;\n    }\n\n    @Override\n    public boolean charTyped(char p_charTyped_1_, int p_charTyped_2_) {\n        if (!this.field.isFocused())\n            return false;\n\n        this.field.charTyped(p_charTyped_1_, p_charTyped_2_);\n        if (this.field.getValue().length() > 1 && this.field.getValue().charAt(0) == '0')\n            this.field.setValue(String.valueOf(this.field.getInt()));\n\n        if (this.field.getInt() > this.max)\n            this.field.setValue(String.valueOf(this.max));\n\n        return true;\n    }\n\n    public int getX() {\n        return x;\n    }\n\n    public int getY() {\n        return y;\n    }\n\n\n    @Override\n    protected void updateWidgetNarration(NarrationElementOutput p_259858_) {\n\n    }\n\n    public interface IIncrementerChanged {\n        void onChange(int value);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/client/screen/widgets/GuiTextFieldBase.java",
    "content": "package com.direwolf20.buildinggadgets.client.screen.widgets;\n\nimport net.minecraft.client.gui.Font;\nimport net.minecraft.client.gui.components.EditBox;\nimport net.minecraft.network.chat.Component;\n\nimport java.util.function.BiConsumer;\n\npublic class GuiTextFieldBase extends EditBox {\n    private boolean suspended;\n    private String valueDefault, valueOld;\n    private BiConsumer<GuiTextFieldBase, String> postModification;\n\n    public GuiTextFieldBase(Font fontRenderer, int x, int y, int width) {\n        super(fontRenderer, x, y, width, 15, Component.empty());\n\n        setMaxLength(50);\n        setFilter(s -> {\n            valueOld = getValue();\n            return true;\n        });\n    }\n\n    @Override\n    public void setValue(String textIn) {\n        super.setValue(textIn);\n\n        //TODO validate that this is the correct place\n        postModification(textIn);\n    }\n\n    public void postModification(String text) {\n        if (!suspended && postModification != null) {\n            suspended = true;\n            postModification.accept(this, valueOld);\n            suspended = false;\n        }\n    }\n\n    public GuiTextFieldBase restrictToNumeric() {\n        setFilter(s -> {\n            valueOld = getValue();\n            if (s == null || s.isEmpty() || \"-\".equals(s))\n                return true;\n\n            try {\n                Integer.parseInt(s);\n                return true;\n            } catch (NumberFormatException e) {\n                return false;\n            }\n        });\n\n        return this;\n    }\n\n    public int getInt() {\n        try {\n            return Integer.parseInt(getValue());\n        } catch (NumberFormatException e) {\n            return 0;\n        }\n    }\n\n    public GuiTextFieldBase setDefaultInt(int defaultInt) {\n        return setDefaultValue(Integer.toString(defaultInt));\n    }\n\n    public GuiTextFieldBase setDefaultValue(String defaultValue) {\n        this.valueDefault = defaultValue;\n        return this;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/client/screen/widgets/IncrementalSliderWidget.java",
    "content": "package com.direwolf20.buildinggadgets.client.screen.widgets;\n\nimport com.direwolf20.buildinggadgets.client.OurSounds;\nimport com.google.common.collect.ImmutableSet;\nimport com.mojang.blaze3d.vertex.PoseStack;\nimport net.minecraft.client.Minecraft;\nimport net.minecraft.client.gui.Font;\nimport net.minecraft.client.gui.components.AbstractWidget;\nimport net.minecraft.client.gui.components.Button;\nimport net.minecraft.client.sounds.SoundManager;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.util.Mth;\nimport net.minecraftforge.client.gui.widget.ForgeSlider;\n\nimport java.awt.*;\nimport java.util.Collection;\nimport java.util.function.Consumer;\n\n/**\n * A flat colored, incremental (+ and - buttons) slider widget\n */\npublic class IncrementalSliderWidget extends ForgeSlider {\n    private static final int BACKGROUND = createAlphaColor(Color.DARK_GRAY, 200).getRGB();\n    private static final int SLIDER_BACKGROUND = createAlphaColor(Color.DARK_GRAY.darker(), 200).getRGB();\n    private static final int SLIDER_COLOR = createAlphaColor(Color.DARK_GRAY.brighter().brighter(), 200).getRGB();\n\n    public final Consumer<IncrementalSliderWidget> onUpdate;\n\n    public IncrementalSliderWidget(int x, int y, int width, int height, double min, double max, Component prefix, double current, Consumer<IncrementalSliderWidget> onUpdate) {\n        super(x, y, width, height, prefix, Component.empty(), min, max, current, 1D, 1, true);\n        this.onUpdate = onUpdate;\n    }\n\n    @Override\n    public void renderWidget(PoseStack poseStack, int mouseX, int mouseY, float partialTick) {\n        fill(poseStack, this.getX(), this.getY(), this.getX() + this.width, this.getY() + this.height, BACKGROUND);\n        this.drawBorderedRect(poseStack, (this.getX() + (int)(this.value * (double)(this.width - 8)) + 4) - 4, this.getY(), 8, this.height);\n        this.renderText(poseStack);\n    }\n\n    private void renderText(PoseStack matrices) {\n        int color = !active ? 10526880 : (isHovered ? 16777120 : -1);\n\n        Minecraft minecraft = Minecraft.getInstance();\n        drawCenteredString(matrices, minecraft.font, this.prefix.copy().append(this.getValueString()), getX() + getWidth() / 2, getY() + (getHeight() - 8) / 2, color);\n    }\n\n    private void drawBorderedRect(PoseStack matrices, int x, int y, int width, int height) {\n        fill(matrices, x, y, x + width, y + height, SLIDER_BACKGROUND);\n        fill(matrices, ++x, ++y, x + width - 2, y + height - 2, SLIDER_COLOR);\n    }\n\n    @Override\n    protected void applyValue() {\n        this.onUpdate.accept(this);\n    }\n\n    @Override\n    public void onRelease(double p_93609_, double p_93610_) {\n    }\n\n    @Override\n    public void playDownSound(SoundManager p_93605_) {\n    }\n\n    @Override\n    public boolean mouseReleased(double p_93684_, double p_93685_, int p_93686_) {\n        var result = super.mouseReleased(p_93684_, p_93685_, p_93686_);\n\n        // Prevents spam of sounds due to the ForgeSlider\n        if (result) {\n            OurSounds.playSound(OurSounds.BEEP.get());\n        }\n\n        return result;\n    }\n\n    private static Color createAlphaColor(Color color, int alpha) {\n        return new Color(color.getRed(), color.getGreen(), color.getBlue(), alpha);\n    }\n\n    // This is lazy, I should really just build it into a single widget render\n    public Collection<AbstractWidget> getComponents() {\n        return ImmutableSet.of(\n                this,\n                new GuiButtonIncrement(getX() - height, getY(), height, height, Component.literal(\"-\"), b -> {\n                    this.setValue(this.getValueInt() - 1);\n                    IncrementalSliderWidget.this.applyValue();\n                }),\n                new GuiButtonIncrement(getX() + width, getY(), height, height, Component.literal(\"+\"), b -> {\n                    this.setValue(this.getValueInt() + 1);\n                    IncrementalSliderWidget.this.applyValue();\n                })\n        );\n    }\n\n    private class GuiButtonIncrement extends Button {\n        public GuiButtonIncrement(int x, int y, int width, int height, Component buttonText, OnPress action) {\n            super(builder(buttonText, action)\n                    .pos(x, y)\n                    .size(width, height));\n        }\n\n        @Override\n        public void render(PoseStack matrices, int mouseX, int mouseY, float partial) {\n            if (!visible)\n                return;\n\n            Minecraft minecraft = Minecraft.getInstance();\n            Font font = minecraft.font;\n\n            this.isHovered = mouseX >= this.getX() && mouseY >= this.getY() && mouseX < this.getX() + this.width && mouseY < this.getY() + this.height;\n            fill(matrices, this.getX(), this.getY(), this.getX() + this.width, this.getY() + this.height, IncrementalSliderWidget.BACKGROUND);\n            IncrementalSliderWidget.this.drawBorderedRect(matrices, this.getX(), this.getY(), this.width, this.height);\n            drawCenteredString(matrices, font, this.getMessage(), this.getX() + this.width / 2, this.getY() + (this.height - 8) / 2, getFGColor() | Mth.ceil(this.alpha * 255.0F) << 24);\n        }\n\n        @Override\n        public void onRelease(double p_93609_, double p_93610_) {\n        }\n\n        @Override\n        public void playDownSound(SoundManager soundManager) {\n            OurSounds.playSound(OurSounds.BEEP.get());\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/BuildingGadgets.java",
    "content": "package com.direwolf20.buildinggadgets.common;\n\nimport com.direwolf20.buildinggadgets.client.ClientProxy;\nimport com.direwolf20.buildinggadgets.client.OurSounds;\nimport com.direwolf20.buildinggadgets.common.blocks.OurBlocks;\nimport com.direwolf20.buildinggadgets.common.commands.ForceUnloadedCommand;\nimport com.direwolf20.buildinggadgets.common.commands.OverrideBuildSizeCommand;\nimport com.direwolf20.buildinggadgets.common.commands.OverrideCopySizeCommand;\nimport com.direwolf20.buildinggadgets.common.config.Config;\nimport com.direwolf20.buildinggadgets.common.config.RecipeConstructionPaste.Serializer;\nimport com.direwolf20.buildinggadgets.common.containers.OurContainers;\nimport com.direwolf20.buildinggadgets.common.entities.OurEntities;\nimport com.direwolf20.buildinggadgets.common.items.AbstractGadget;\nimport com.direwolf20.buildinggadgets.common.items.OurItems;\nimport com.direwolf20.buildinggadgets.common.network.PacketHandler;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.InventoryHelper;\nimport com.direwolf20.buildinggadgets.common.tainted.registry.Registries;\nimport com.direwolf20.buildinggadgets.common.tainted.save.SaveManager;\nimport com.direwolf20.buildinggadgets.common.tainted.template.ITemplateKey;\nimport com.direwolf20.buildinggadgets.common.tainted.template.ITemplateProvider;\nimport com.direwolf20.buildinggadgets.common.tileentities.OurTileEntities;\nimport com.direwolf20.buildinggadgets.common.util.ref.NBTKeys;\nimport com.direwolf20.buildinggadgets.common.util.ref.Reference;\nimport net.minecraft.commands.Commands;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.resources.ResourceLocation;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.crafting.RecipeSerializer;\nimport net.minecraftforge.common.MinecraftForge;\nimport net.minecraftforge.common.capabilities.RegisterCapabilitiesEvent;\nimport net.minecraftforge.event.CreativeModeTabEvent;\nimport net.minecraftforge.event.RegisterCommandsEvent;\nimport net.minecraftforge.event.server.ServerStartedEvent;\nimport net.minecraftforge.event.server.ServerStoppedEvent;\nimport net.minecraftforge.eventbus.api.IEventBus;\nimport net.minecraftforge.eventbus.api.SubscribeEvent;\nimport net.minecraftforge.fml.InterModComms;\nimport net.minecraftforge.fml.ModLoadingContext;\nimport net.minecraftforge.fml.common.Mod;\nimport net.minecraftforge.fml.config.ModConfig.Type;\nimport net.minecraftforge.fml.event.lifecycle.*;\nimport net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;\nimport net.minecraftforge.registries.DeferredRegister;\nimport net.minecraftforge.registries.ForgeRegistries;\nimport net.minecraftforge.registries.RegistryObject;\nimport org.apache.logging.log4j.LogManager;\nimport org.apache.logging.log4j.Logger;\n\n@Mod(Reference.MODID)\npublic final class BuildingGadgets {\n    public static Logger LOG = LogManager.getLogger();\n\n    private static final DeferredRegister<RecipeSerializer<?>> RECIPE_SERIALIZER = DeferredRegister.create(ForgeRegistries.RECIPE_SERIALIZERS, Reference.MODID);\n    public static RegistryObject<RecipeSerializer<?>> CONSTRUCTION_PASTE_RECIPE_SERIALIZER = RECIPE_SERIALIZER.register(\"construction_paste\", () -> Serializer.INSTANCE);\n\n    public BuildingGadgets() {\n        IEventBus eventBus = FMLJavaModLoadingContext.get().getModEventBus();\n\n        ModLoadingContext.get().registerConfig(Type.SERVER, Config.SERVER_CONFIG);\n        ModLoadingContext.get().registerConfig(Type.CLIENT, Config.CLIENT_CONFIG);\n\n        OurBlocks.BLOCKS.register(eventBus);\n        OurItems.ITEMS.register(eventBus);\n        OurTileEntities.TILE_ENTITIES.register(eventBus);\n        OurContainers.CONTAINERS.register(eventBus);\n\n        OurSounds.REGISTRY.register(eventBus);\n        OurEntities.ENTITY_REGISTER.register(eventBus);\n\n        RECIPE_SERIALIZER.register(eventBus);\n        Registries.TILE_DATA_SERIALIZER_DEFERRED_REGISTER.register(eventBus);\n        Registries.UNIQUE_OBJECT_SERIALIZER_DEFERRED_REGISTER.register(eventBus);\n\n        MinecraftForge.EVENT_BUS.addListener(this::serverLoaded);\n        MinecraftForge.EVENT_BUS.addListener(this::serverStopped);\n        MinecraftForge.EVENT_BUS.addListener(this::commandRegister);\n\n        eventBus.addListener(this::setup);\n        eventBus.addListener(this::clientSetup);\n        eventBus.addListener(this::loadComplete);\n        eventBus.addListener(this::handleIMC);\n        eventBus.addListener(this::onEnqueueIMC);\n        eventBus.addListener(this::registerCaps);\n        eventBus.addListener(this::registerCreativeTab);\n    }\n\n    private void registerCreativeTab(CreativeModeTabEvent.Register register) {\n        register.registerCreativeModeTab(new ResourceLocation(Reference.MODID, Reference.MODID), builder ->\n            builder\n                .title(Component.translatable(\"itemGroup.\" + Reference.MODID))\n                .icon(() -> {\n                    ItemStack stack = new ItemStack(OurItems.BUILDING_GADGET_ITEM.get());\n                    stack.getOrCreateTag().putByte(NBTKeys.CREATIVE_MARKER, (byte) 0);\n                    return stack;\n                })\n                .displayItems((featureFlags, output) -> {\n                    // Register it alL!\n                    OurItems.ITEMS.getEntries()\n                            .forEach(e -> {\n                                if (e.get() instanceof AbstractGadget) {\n                                    output.accept(e.get());\n\n                                    // Create charged versions of the gadgets :D\n                                    ItemStack stack = new ItemStack(e.get());\n                                    stack.getOrCreateTag().putInt(NBTKeys.ENERGY, ((AbstractGadget) e.get()).getEnergyMax());\n                                    output.accept(stack);\n                                } else {\n                                    output.accept(e.get());\n                                }\n                            });\n\n                    // Some of the blocks too!\n                    OurBlocks.BLOCKS.getEntries()\n                            .stream().filter(e -> e != OurBlocks.EFFECT_BLOCK)\n                            .forEach(e -> output.accept(e.get()));\n                })\n            .build());\n    }\n\n    private void registerCaps(RegisterCapabilitiesEvent event) {\n        event.register(ITemplateProvider.class);\n        event.register(ITemplateKey.class);\n    }\n\n    private void clientSetup(final FMLClientSetupEvent event) {\n        ClientProxy.clientSetup();\n    }\n\n    private void setup(final FMLCommonSetupEvent event) {\n        PacketHandler.register();\n    }\n\n    private void loadComplete(FMLLoadCompleteEvent event) {\n        Registries.createOrderedRegistries();\n    }\n\n    private void handleIMC(InterModProcessEvent event) {\n        event.getIMCStream().forEach(this::handleIMCMessage);\n    }\n\n    private void handleIMCMessage(InterModComms.IMCMessage message) {\n        if (Registries.handleIMC(message))\n            LOG.trace(\"Successfully handled IMC-Message using Method {} from Mod {}.\", message.getMethod(), message.getSenderModId());\n        else\n            LOG.warn(\"Failed to handle IMC-Message using Method {} from Mod {}!\", message.getMethod(), message.getSenderModId());\n    }\n\n    @SubscribeEvent\n    public void commandRegister(RegisterCommandsEvent event) {\n        event.getDispatcher().register(\n                Commands.literal(Reference.MODID)\n                        .then(OverrideBuildSizeCommand.registerToggle())\n                        .then(OverrideCopySizeCommand.registerToggle())\n                        .then(ForceUnloadedCommand.registerToggle())\n                        .then(OverrideBuildSizeCommand.registerList())\n                        .then(OverrideCopySizeCommand.registerList())\n                        .then(ForceUnloadedCommand.registerList())\n        );\n    }\n\n    private void serverLoaded(ServerStartedEvent event) {\n        SaveManager.INSTANCE.onServerStarted(event);\n    }\n\n    private void serverStopped(ServerStoppedEvent event) {\n        SaveManager.INSTANCE.onServerStopped(event);\n    }\n\n    private void onEnqueueIMC(InterModEnqueueEvent event) {\n        InventoryHelper.registerHandleProviders();\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/blocks/ConstructionBlock.java",
    "content": "package com.direwolf20.buildinggadgets.common.blocks;\n\nimport com.direwolf20.buildinggadgets.common.tileentities.ConstructionBlockTileEntity;\nimport com.direwolf20.buildinggadgets.common.tileentities.OurTileEntities;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.world.entity.Entity;\nimport net.minecraft.world.level.BlockGetter;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.Block;\nimport net.minecraft.world.level.block.Blocks;\nimport net.minecraft.world.level.block.EntityBlock;\nimport net.minecraft.world.level.block.RenderShape;\nimport net.minecraft.world.level.block.entity.BlockEntity;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.level.block.state.StateDefinition;\nimport net.minecraft.world.level.block.state.properties.BooleanProperty;\nimport net.minecraft.world.level.block.state.properties.Property;\nimport net.minecraft.world.level.material.Material;\nimport net.minecraft.world.level.pathfinder.PathComputationType;\nimport net.minecraft.world.phys.shapes.CollisionContext;\nimport net.minecraft.world.phys.shapes.Shapes;\nimport net.minecraft.world.phys.shapes.VoxelShape;\nimport net.minecraftforge.api.distmarker.Dist;\nimport net.minecraftforge.api.distmarker.OnlyIn;\n\nimport javax.annotation.Nullable;\n\n//@Optional.Interface(iface = \"team.chisel.ctm.api.IFacade\", modid = \"ctm-api\")\npublic class ConstructionBlock extends Block implements EntityBlock /*implements IFacade*/ {\n    //TODO Review which methods we are overriding -- a LOT more were added at some point, many of which I probably don't want in here :).\n    public static final Property<Boolean> BRIGHT = BooleanProperty.create(\"bright\");\n    public static final Property<Boolean> NEIGHBOR_BRIGHTNESS = BooleanProperty.create(\"neighbor_brightness\");\n    public static final Property<Boolean> AMBIENT_OCCLUSION = BooleanProperty.create(\"ambient_occlusion\");\n\n    public ConstructionBlock() {\n        super(Block.Properties.of(Material.SAND).strength(1.5F, 6.0F));\n        registerDefaultState(this.getStateDefinition().any().setValue(BRIGHT, true).setValue(NEIGHBOR_BRIGHTNESS, false).setValue(AMBIENT_OCCLUSION, false));\n    }\n\n//    @Override\n//    public boolean addDestroyEffects(BlockState state, Level world, BlockPos pos, ParticleEngine manager) {\n//        BlockEntity tileEntity = world.getBlockEntity(pos);\n//        if (!(tileEntity instanceof ConstructionBlockTileEntity)) {\n//            return super.addDestroyEffects(state, world, pos, manager);\n//        }\n//\n//        manager.destroy(pos, tileEntity.getBlockState());\n//        return true;\n//    }\n//\n//    @Override\n//    public boolean addHitEffects(BlockState state, Level world, HitResult target, ParticleEngine manager) {\n//        if (target.getType() != HitResult.Type.BLOCK) {\n//            return super.addHitEffects(state, world, target, manager);\n//        }\n//\n//        BlockPos pos = ((BlockHitResult) target).getBlockPos();\n//        BlockEntity tileEntity = world.getBlockEntity(pos);\n//        if (!(tileEntity instanceof ConstructionBlockTileEntity)) {\n//            return super.addHitEffects(state, world, target, manager);\n//        }\n//\n//        this.addBlockHitEffects(world, pos, tileEntity.getBlockState(), ((BlockHitResult) target).getDirection(), manager);\n//        return true;\n//    }\n\n//    /**\n//     * Stolen from the particle manager to handle the custom state (minified code a bit)\n//     */\n//    public void addBlockHitEffects(Level world, BlockPos pos, BlockState blockstate, Direction side, ParticleEngine manager) {\n//        if (blockstate.getRenderShape() == RenderShape.INVISIBLE) return;\n//        Random rand = new Random();\n//        int i = pos.getX(), j = pos.getY(), k = pos.getZ();\n//        AABB axisalignedbb = blockstate.getShape(world, pos).bounds();\n//        double x = (double)i + rand.nextDouble() * (axisalignedbb.maxX - axisalignedbb.minX - (double)0.2F) + (double)0.1F + axisalignedbb.minX;\n//        double y = (double)j + rand.nextDouble() * (axisalignedbb.maxY - axisalignedbb.minY - (double)0.2F) + (double)0.1F + axisalignedbb.minY;\n//        double z = (double)k + rand.nextDouble() * (axisalignedbb.maxZ - axisalignedbb.minZ - (double)0.2F) + (double)0.1F + axisalignedbb.minZ;\n//        if (side == Direction.DOWN || side == Direction.UP)\n//            y = side == Direction.DOWN ? (double)j + axisalignedbb.minY - (double)0.1F : (double)j + axisalignedbb.maxY + (double)0.1F;\n//        if (side == Direction.NORTH || side == Direction.SOUTH)\n//            z = side == Direction.NORTH ? (double)k + axisalignedbb.minZ - (double)0.1F : (double)k + axisalignedbb.maxZ + (double)0.1F;\n//        if (side == Direction.WEST || side == Direction.EAST)\n//            x = side == Direction.WEST ? (double)i + axisalignedbb.minX - (double)0.1F : (double)i + axisalignedbb.maxX + (double)0.1F;\n//        manager.add((new TerrainParticle((ClientLevel) world, x, y, z, 0.0D, 0.0D, 0.0D, blockstate)).init(pos).setPower(0.2F).scale(0.6F));\n//    }\n\n\n    @Override\n    public boolean hasDynamicShape() {\n        return true;\n    }\n\n    @Override\n    protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {\n        super.createBlockStateDefinition(builder);\n        builder.add(BRIGHT, NEIGHBOR_BRIGHTNESS, AMBIENT_OCCLUSION);\n    }\n\n    @Nullable\n    @Override\n    public BlockEntity newBlockEntity(BlockPos blockPos, BlockState blockState) {\n        return OurTileEntities.CONSTRUCTION_BLOCK_TILE_ENTITY.get().create(blockPos, blockState);\n    }\n\n    public boolean isMimicNull(BlockState mimicBlock) {\n        return (mimicBlock == null || mimicBlock == Blocks.AIR.defaultBlockState());\n    }\n\n    @Nullable\n    public static BlockState getActualMimicBlock(BlockGetter blockAccess, BlockPos pos) {\n        BlockEntity te = blockAccess.getBlockEntity(pos);\n        if (te instanceof ConstructionBlockTileEntity) {\n            return ((ConstructionBlockTileEntity) te).getConstructionBlockData().getState();\n        }\n        return null;\n    }\n\n    @Override\n    @Deprecated\n    public RenderShape getRenderShape(BlockState state) {\n        return RenderShape.MODEL;\n    }\n\n    @Override\n    public int getLightBlock(BlockState state, BlockGetter worldIn, BlockPos pos) {\n        Boolean bright = state.getValue(ConstructionBlock.BRIGHT);\n        if (bright) {\n            return 0;\n        }\n        return 255;\n    }\n\n// 1.14 code\n//    @Override\n//    public boolean doesSideBlockRendering(BlockState state, IBlockReader world, BlockPos pos, Direction face) {\n//        BlockState mimic = getActualMimicBlock(world, pos);\n//        return !isMimicNull(mimic) ? mimic.doesSideBlockRendering(world, pos, face) : super.doesSideBlockRendering(state, world, pos, face);\n//    }\n\n    @Override\n    public VoxelShape getCollisionShape(BlockState state, BlockGetter worldIn, BlockPos pos, CollisionContext selectionContext) {\n        BlockState mimic = getActualMimicBlock(worldIn, pos);\n        return !isMimicNull(mimic) ? mimic.getCollisionShape(worldIn, pos) : super.getCollisionShape(state, worldIn, pos, selectionContext);\n    }\n\n    @Override\n    public void entityInside(BlockState state, Level worldIn, BlockPos pos, Entity entityIn) {\n        BlockState mimic = getActualMimicBlock(worldIn, pos);\n        if (!isMimicNull(mimic)) {\n            mimic.entityInside(worldIn, pos, entityIn);\n        } else {\n            super.entityInside(state, worldIn, pos, entityIn);\n        }\n    }\n\n    @Override\n    public boolean isPathfindable(BlockState state, BlockGetter worldIn, BlockPos pos, PathComputationType type) {\n\n        BlockState mimic = getActualMimicBlock(worldIn, pos);\n        return !isMimicNull(mimic) ? mimic.isPathfindable(worldIn, pos, type) : super.isPathfindable(state, worldIn, pos, type);\n    }\n\n    @Override\n    public boolean skipRendering(BlockState state, BlockState adjacentBlockState, Direction side) {\n        if (state.getBlock().equals(adjacentBlockState.getBlock())) return false;\n        return super.skipRendering(state, adjacentBlockState, side); //TODO Find a way to make this work, glass adjacent to fake glass shows the middle side.\n        /*boolean bright = state.get(ConstructionBlock.BRIGHT);\n        if (!bright) return false;\n        if (adjacentBlockState.getBlock() instanceof ConstructionBlock) {\n            return !adjacentBlockState.get(ConstructionBlock.BRIGHT);\n        } else {\n            //This is how vanilla BreakableBlock does it.\n            return adjacentBlockState.getBlock() == this ? true : super.isSideInvisible(state, adjacentBlockState, side);\n        }*/\n    }\n\n    @Override\n    public VoxelShape getShape(BlockState state, BlockGetter worldIn, BlockPos pos, CollisionContext selectionContext) {\n        BlockState mimic = getActualMimicBlock(worldIn, pos);\n        return !isMimicNull(mimic) ? mimic.getShape(worldIn, pos) : super.getShape(state, worldIn, pos, selectionContext);\n    }\n\n    @Override\n    public VoxelShape getOcclusionShape(BlockState state, BlockGetter worldIn, BlockPos pos) {\n        BlockState mimic = getActualMimicBlock(worldIn, pos);\n        if (!isMimicNull(mimic) && !mimic.canOcclude()) {\n            return Shapes.empty();\n        }\n        return !isMimicNull(mimic) ? mimic.getBlockSupportShape(worldIn, pos) : super.getOcclusionShape(state, worldIn, pos);\n    }\n\n    @Override\n    public VoxelShape getInteractionShape(BlockState state, BlockGetter worldIn, BlockPos pos) {\n        BlockState mimic = getActualMimicBlock(worldIn, pos);\n        // dummy is likely wrong.\n        return !isMimicNull(mimic) ? mimic.getVisualShape(worldIn, pos, CollisionContext.empty()) : super.getInteractionShape(state, worldIn, pos);\n    }\n\n    @Override\n    public boolean propagatesSkylightDown(BlockState state, BlockGetter reader, BlockPos pos) {\n        BlockState mimic = getActualMimicBlock(reader, pos);\n        return !isMimicNull(mimic) ? mimic.propagatesSkylightDown(reader, pos) : super.propagatesSkylightDown(state, reader, pos);\n    }\n\n    // Todo re-eval\n   /* @Override\n    @OnlyIn(Dist.CLIENT)\n    public static boolean shouldSideBeRendered(BlockState adjacentState, IBlockReader blockState, BlockPos blockAccess, Direction pos) {\n        //FakeRenderWorld fakeWorld = new FakeRenderWorld();\n\n        BlockState mimicBlock = getActualMimicBlock(blockState, blockAccess);\n        if (mimicBlock == null) {\n            return true;\n        }\n        BlockState sideBlockState = blockState.getBlockState(blockAccess.offset(pos));\n        if (sideBlockState.getBlock().equals(OurBlocks.constructionBlock)) {\n            if (!(getActualMimicBlock(blockState, blockAccess.offset(pos)) == null)) {\n                sideBlockState = getActualMimicBlock(blockState, blockAccess.offset(pos));\n            }\n        }\n\n        try {\n            return mimicBlock.getBlock().shouldSideBeRendered(sideBlockState, blockState, blockAccess, pos);\n        } catch (Exception var8) {\n            return true;\n        }\n    }*/\n\n\n// todo: removed 1.16 - find replacement\n//    @Override\n//    public boolean isNormalCube(BlockState state, IBlockReader world, BlockPos pos) {\n//        BlockState mimicBlock = getActualMimicBlock(world, pos);\n//        if (mimicBlock == null) {\n//            return super.isNormalCube(state, world, pos);\n//        }\n//        try {\n//            return mimicBlock.getBlock().isNormalCube(mimicBlock, world, pos);\n//        } catch (Exception var8) {\n//            return super.isNormalCube(state, world, pos);\n//        }\n//    }\n    @Deprecated\n    @OnlyIn(Dist.CLIENT)\n    public float getShadeBrightness(BlockState state, BlockGetter worldIn, BlockPos pos) {\n        Boolean bright = state.getValue(ConstructionBlock.BRIGHT);\n        Boolean neighborBrightness = state.getValue(ConstructionBlock.NEIGHBOR_BRIGHTNESS);\n        if (bright || neighborBrightness) {\n            return 1f;\n        }\n        return 0.2f;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/blocks/ConstructionBlockDense.java",
    "content": "package com.direwolf20.buildinggadgets.common.blocks;\n\nimport net.minecraft.world.level.block.Block;\nimport net.minecraft.world.level.material.Material;\n\npublic class ConstructionBlockDense extends Block {\n    public ConstructionBlockDense() {\n        super(Block.Properties.of(Material.STONE).strength(3f, 0f));\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/blocks/ConstructionBlockPowder.java",
    "content": "package com.direwolf20.buildinggadgets.common.blocks;\n\nimport com.direwolf20.buildinggadgets.common.entities.ConstructionBlockEntity;\nimport com.direwolf20.buildinggadgets.common.util.lang.TooltipTranslation;\nimport net.minecraft.world.level.block.Block;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.level.block.FallingBlock;\nimport net.minecraft.world.level.material.Material;\nimport net.minecraft.world.item.TooltipFlag;\nimport net.minecraft.world.entity.item.FallingBlockEntity;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.tags.FluidTags;\nimport net.minecraft.core.Direction;\nimport net.minecraft.world.phys.AABB;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.world.level.BlockGetter;\nimport net.minecraft.world.level.Level;\n\nimport javax.annotation.Nullable;\nimport java.util.List;\n\npublic class ConstructionBlockPowder extends FallingBlock {\n    public ConstructionBlockPowder() {\n        super(Block.Properties.of(Material.SAND).strength(0.5F));\n    }\n\n    @Override\n    public void appendHoverText(ItemStack stack, @Nullable BlockGetter worldIn, List<Component> tooltip, TooltipFlag flagIn) {\n        super.appendHoverText(stack, worldIn, tooltip, flagIn);\n        tooltip.add(TooltipTranslation.CONSTRUCTIONBLOCKPOWDER_HELPTEXT.componentTranslation());\n    }\n\n    @Override\n    public void onLand(Level worldIn, BlockPos pos, BlockState p_176502_3_, BlockState p_176502_4_, FallingBlockEntity p_176502_5_) {\n        if (worldIn.getFluidState(pos).is(FluidTags.WATER))\n            worldIn.addFreshEntity(new ConstructionBlockEntity(worldIn, pos, true));\n    }\n\n    private boolean tryTouchWater(Level worldIn, BlockPos pos) {\n        boolean foundWater = false;\n\n        for (Direction enumFacing : Direction.values()) {\n            if (enumFacing != Direction.DOWN && worldIn.getFluidState(pos.relative(enumFacing)).is(FluidTags.WATER)) {\n                foundWater = true;\n                break;\n            }\n        }\n\n        if (foundWater) {\n            if (worldIn.getEntitiesOfClass(ConstructionBlockEntity.class, new AABB(pos.getX() - 0.5, pos.getY() - 0.5, pos.getZ() - 0.5, pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5)).isEmpty()) {\n                worldIn.addFreshEntity(new ConstructionBlockEntity(worldIn, pos, true));\n            }\n        }\n\n        return foundWater;\n    }\n\n    @Override\n    public void neighborChanged(BlockState state, Level world, BlockPos pos, Block block, BlockPos fromPos, boolean __a) {\n        tryTouchWater(world, pos);\n    }\n\n    @Override\n    public void onPlace(BlockState state, Level worldIn, BlockPos pos, BlockState oldState, boolean __a) {\n        if (!this.tryTouchWater(worldIn, pos)) {\n            super.onPlace(state, worldIn, pos, oldState, __a);\n        }\n    }\n\n    @Override\n    public int getLightBlock(BlockState state, BlockGetter worldIn, BlockPos pos) {\n        return 0;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/blocks/EffectBlock.java",
    "content": "package com.direwolf20.buildinggadgets.common.blocks;\n\nimport com.direwolf20.buildinggadgets.common.entities.ConstructionBlockEntity;\nimport com.direwolf20.buildinggadgets.common.tainted.building.BlockData;\nimport com.direwolf20.buildinggadgets.common.tainted.building.PlacementTarget;\nimport com.direwolf20.buildinggadgets.common.tainted.building.tilesupport.TileSupport;\nimport com.direwolf20.buildinggadgets.common.tainted.building.view.BuildContext;\nimport com.direwolf20.buildinggadgets.common.tileentities.ConstructionBlockTileEntity;\nimport com.direwolf20.buildinggadgets.common.tileentities.EffectBlockTileEntity;\nimport com.direwolf20.buildinggadgets.common.tileentities.OurTileEntities;\nimport net.minecraft.MethodsReturnNonnullByDefault;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.BlockGetter;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.LevelAccessor;\nimport net.minecraft.world.level.block.*;\nimport net.minecraft.world.level.block.entity.BlockEntity;\nimport net.minecraft.world.level.block.entity.BlockEntityTicker;\nimport net.minecraft.world.level.block.entity.BlockEntityType;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.level.material.Material;\nimport net.minecraft.world.level.material.MaterialColor;\nimport net.minecraft.world.level.material.PushReaction;\nimport net.minecraft.world.level.storage.loot.LootContext;\nimport net.minecraft.world.phys.shapes.CollisionContext;\nimport net.minecraft.world.phys.shapes.Shapes;\nimport net.minecraft.world.phys.shapes.VoxelShape;\nimport net.minecraftforge.api.distmarker.Dist;\nimport net.minecraftforge.api.distmarker.OnlyIn;\n\nimport javax.annotation.Nullable;\nimport javax.annotation.ParametersAreNonnullByDefault;\nimport java.util.ArrayList;\nimport java.util.List;\n\n@MethodsReturnNonnullByDefault\n@ParametersAreNonnullByDefault\npublic class EffectBlock extends BaseEntityBlock {\n\n    public enum Mode {\n        // Serialization and networking based on `ordinal()`, please DO NOT CHANGE THE ORDER of the enums\n        PLACE() {\n            @Override\n            public void onBuilderRemoved(EffectBlockTileEntity builder) {\n                Level world = builder.getLevel();\n                if (world == null)\n                    return;\n\n                BlockPos targetPos = builder.getBlockPos();\n                BlockData targetBlock = builder.getRenderedBlock();\n                if (builder.isUsingPaste()) {\n                    world.setBlockAndUpdate(targetPos, OurBlocks.CONSTRUCTION_BLOCK.get().defaultBlockState());\n                    BlockEntity te = world.getBlockEntity(targetPos);\n                    if (te instanceof ConstructionBlockTileEntity) {\n                        ((ConstructionBlockTileEntity) te).setBlockState(targetBlock);\n                    }\n                    world.addFreshEntity(new ConstructionBlockEntity(world, targetPos, false));\n                } else {\n                    if (targetBlock.getState().getBlock() instanceof LeavesBlock) {\n                        targetBlock = new BlockData(targetBlock.getState().setValue(LeavesBlock.PERSISTENT, true), targetBlock.getTileData());\n                    }\n\n                    targetBlock.placeIn(BuildContext.builder().build(world), targetPos);\n\n                    // Instead of removing the block, we just sync the client & server to know that the block has been replaced\n                    world.sendBlockUpdated(targetPos, targetBlock.getState(), targetBlock.getState(), Block.UPDATE_ALL_IMMEDIATE);\n\n                    BlockPos upPos = targetPos.above();\n                    world.getBlockState(targetPos).neighborChanged(world, targetPos, world.getBlockState(upPos).getBlock(), upPos, false);\n                }\n            }\n        },\n        REMOVE() {\n            @Override\n            public void onBuilderRemoved(EffectBlockTileEntity builder) {\n                builder.getLevel().removeBlock(builder.getBlockPos(), false);\n            }\n        },\n        REPLACE() {\n            @Override\n            public void onBuilderRemoved(EffectBlockTileEntity builder) {\n                spawnEffectBlock(builder.getLevel(), builder.getBlockPos(), builder.getSourceBlock(), PLACE, builder.isUsingPaste());\n            }\n        };\n\n        public static final Mode[] VALUES = values();\n\n        public abstract void onBuilderRemoved(EffectBlockTileEntity builder);\n    }\n\n    @Nullable\n    @Override\n    public <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level level, BlockState state, BlockEntityType<T> type) {\n        return createTickerHelper(type, OurTileEntities.EFFECT_BLOCK_TILE_ENTITY.get(), EffectBlockTileEntity::tick);\n    }\n\n    /**\n     * As the effect block is effectively air it needs to have a material just like Air.\n     * We don't use Material.AIR as this is replaceable.\n     */\n    private static final Material EFFECT_BLOCK_MATERIAL = new Material.Builder(MaterialColor.NONE).nonSolid().build();\n\n    public static void spawnUndoBlock(BuildContext context, PlacementTarget target) {\n        BlockState state = context.getWorld().getBlockState(target.getPos());\n\n        BlockEntity curTe = context.getWorld().getBlockEntity(target.getPos());\n        //can't use .isAir, because it's not build yet\n        if (target.getData().getState() != Blocks.AIR.defaultBlockState()) {\n            Mode mode = state.isAir() ? Mode.PLACE : Mode.REPLACE;\n            spawnEffectBlock(curTe, state, context.getWorld(), target.getPos(), target.getData(), mode, false);\n        } else if (!state.isAir()) {\n            spawnEffectBlock(curTe, state, context.getWorld(), target.getPos(), TileSupport.createBlockData(state, curTe), Mode.REMOVE, false);\n        }\n    }\n\n    public static void spawnEffectBlock(BuildContext context, PlacementTarget target, Mode mode, boolean usePaste) {//TODO pass the buildcontext through, aka invert the overloading\n        spawnEffectBlock(context.getWorld(), target.getPos(), target.getData(), mode, usePaste);\n    }\n\n    public static void spawnEffectBlock(LevelAccessor world, BlockPos spawnPos, BlockData spawnBlock, Mode mode, boolean usePaste) {\n        BlockState state = world.getBlockState(spawnPos);\n        BlockEntity curTe = world.getBlockEntity(spawnPos);\n        spawnEffectBlock(curTe, state, world, spawnPos, spawnBlock, mode, usePaste);\n    }\n\n    private static void spawnEffectBlock(@Nullable BlockEntity curTe, BlockState curState, LevelAccessor world, BlockPos spawnPos, BlockData spawnBlock, Mode mode, boolean usePaste) {\n        BlockState state = OurBlocks.EFFECT_BLOCK.get().defaultBlockState();\n        world.setBlock(spawnPos, state, 3);\n\n        BlockEntity tile = world.getBlockEntity(spawnPos);\n        if (!(tile instanceof EffectBlockTileEntity)) {\n            // Fail safely by replacing with air. Kinda voids but meh...\n            world.setBlock(spawnPos, Blocks.AIR.defaultBlockState(), 3);\n            return;\n        }\n\n        ((EffectBlockTileEntity) tile).initializeData(curState, curTe, spawnBlock, mode, usePaste);\n        // Send data to client\n        if (world instanceof Level)\n            ((Level) world).sendBlockUpdated(spawnPos, state, state, Block.UPDATE_ALL_IMMEDIATE);\n    }\n\n    public EffectBlock() {\n        super(Block.Properties.of(EFFECT_BLOCK_MATERIAL)\n                .strength(20f));\n    }\n\n    @Nullable\n    @Override\n    public BlockEntity newBlockEntity(BlockPos pos, BlockState state) {\n        return OurTileEntities.EFFECT_BLOCK_TILE_ENTITY.get().create(pos, state);\n    }\n\n    @Override\n    public VoxelShape getOcclusionShape(BlockState state, BlockGetter worldIn, BlockPos pos) {\n        return Shapes.empty();\n    }\n\n    @Override\n    public VoxelShape getShape(BlockState state, BlockGetter worldIn, BlockPos pos, CollisionContext context) {\n        return Shapes.block();\n    }\n\n    /**\n     * @param state blockState\n     * @return Render Type\n     * @deprecated call via {@link BlockState#getRenderShape()} ()} whenever possible. Implementing/overriding is fine.\n     */\n    @Override\n    @Deprecated\n    @SuppressWarnings(\"deprecation\")\n    public RenderShape getRenderShape(BlockState state) {\n        // We still make effect blocks invisible because all effects (scaling block, transparent box) are dynamic so they has to be in the TER\n        return RenderShape.INVISIBLE;\n    }\n\n    @Override\n    public boolean skipRendering(BlockState p_200122_1_, BlockState p_200122_2_, Direction p_200122_3_) {\n        return true;\n    }\n\n    /**\n     * This gets a complete list of items dropped from this block.\n     *\n     * @param p_220076_1_ Current state\n     */\n    @Override\n    public List<ItemStack> getDrops(BlockState p_220076_1_, LootContext.Builder p_220076_2_) {\n        return new ArrayList<>();\n    }\n\n    /**\n     * @deprecated call via {@link BlockState#getPistonPushReaction()} whenever possible. Implementing/overriding is fine.\n     */\n    @Override\n    @Deprecated\n    public PushReaction getPistonPushReaction(BlockState state) {\n        return PushReaction.BLOCK;\n    }\n\n    @Override\n    public int getLightBlock(BlockState state, BlockGetter worldIn, BlockPos pos) {\n        return 0;\n    }\n\n    @Override\n    @OnlyIn(Dist.CLIENT)\n    public float getShadeBrightness(BlockState state, BlockGetter worldIn, BlockPos pos) {\n        return 1.0f;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/blocks/OurBlocks.java",
    "content": "package com.direwolf20.buildinggadgets.common.blocks;\n\nimport com.direwolf20.buildinggadgets.common.util.ref.Reference;\nimport net.minecraft.world.level.block.Block;\nimport net.minecraftforge.registries.DeferredRegister;\nimport net.minecraftforge.registries.ForgeRegistries;\nimport net.minecraftforge.registries.RegistryObject;\n\npublic final class OurBlocks {\n    private OurBlocks() {}\n\n    public static final DeferredRegister<Block> BLOCKS = DeferredRegister.create(ForgeRegistries.BLOCKS, Reference.MODID);\n\n    public static final RegistryObject<Block> EFFECT_BLOCK = BLOCKS.register(\"effect_block\", EffectBlock::new);\n    public static final RegistryObject<Block> CONSTRUCTION_BLOCK = BLOCKS.register(\"construction_block\", ConstructionBlock::new);\n    public static final RegistryObject<Block> CONSTRUCTION_DENSE_BLOCK = BLOCKS.register(\"construction_block_dense\", ConstructionBlockDense::new);\n    public static final RegistryObject<Block> CONSTRUCTION_POWDER_BLOCK = BLOCKS.register(\"construction_block_powder\", ConstructionBlockPowder::new);\n    public static final RegistryObject<Block> TEMPLATE_MANGER_BLOCK = BLOCKS.register(\"template_manager\", TemplateManager::new);\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/blocks/TemplateManager.java",
    "content": "package com.direwolf20.buildinggadgets.common.blocks;\n\nimport com.direwolf20.buildinggadgets.common.capability.CapabilityTemplate;\nimport com.direwolf20.buildinggadgets.common.tileentities.OurTileEntities;\nimport com.direwolf20.buildinggadgets.common.tileentities.TemplateManagerTileEntity;\nimport com.direwolf20.buildinggadgets.common.util.GadgetUtils;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.minecraft.world.InteractionHand;\nimport net.minecraft.world.InteractionResult;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.context.BlockPlaceContext;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.Block;\nimport net.minecraft.world.level.block.EntityBlock;\nimport net.minecraft.world.level.block.entity.BlockEntity;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.level.block.state.StateDefinition;\nimport net.minecraft.world.level.block.state.properties.BlockStateProperties;\nimport net.minecraft.world.level.block.state.properties.DirectionProperty;\nimport net.minecraft.world.level.material.Material;\nimport net.minecraft.world.phys.BlockHitResult;\nimport net.minecraftforge.common.capabilities.ForgeCapabilities;\nimport net.minecraftforge.network.NetworkHooks;\nimport net.minecraftforge.network.PacketDistributor;\n\nimport javax.annotation.Nullable;\n\npublic class TemplateManager extends Block implements EntityBlock {\n    public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING;\n\n    public TemplateManager() {\n        super(Block.Properties.of(Material.STONE).strength(2f));\n        registerDefaultState(getStateDefinition().any().setValue(FACING, Direction.SOUTH));\n    }\n\n    @Override\n    protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {\n        super.createBlockStateDefinition(builder);\n        builder.add(FACING);\n    }\n\n    @Override\n    public void onRemove(BlockState state, Level worldIn, BlockPos pos, BlockState newState, boolean isMoving) {\n        if (newState.getBlock() != this) {\n            GadgetUtils.dropTileEntityInventory(worldIn, pos);\n            super.onRemove(state, worldIn, pos, newState, isMoving);\n        }\n    }\n\n    @Nullable\n    @Override\n    public BlockState getStateForPlacement(BlockPlaceContext context) {\n        return defaultBlockState().setValue(FACING, context.getHorizontalDirection().getOpposite());\n    }\n\n    @Nullable\n    @Override\n    public BlockEntity newBlockEntity(BlockPos blockPos, BlockState blockState) {\n        return OurTileEntities.TEMPLATE_MANAGER_TILE_ENTITY.get().create(blockPos, blockState);\n    }\n\n    @Override\n    public InteractionResult use(BlockState state, Level worldIn, BlockPos pos, Player player, InteractionHand handIn, BlockHitResult hit) {\n        if (worldIn.isClientSide)\n            return InteractionResult.SUCCESS;\n\n        BlockEntity te = worldIn.getBlockEntity(pos);\n        if (! (te instanceof TemplateManagerTileEntity))\n            return InteractionResult.FAIL;\n\n        te.getCapability(ForgeCapabilities.ITEM_HANDLER).ifPresent(handler -> {\n            worldIn.getCapability(CapabilityTemplate.TEMPLATE_PROVIDER_CAPABILITY).ifPresent(provider -> {\n                for (int i = 0; i < handler.getSlots(); i++) {\n                    ItemStack itemStack = handler.getStackInSlot(i);\n                    itemStack.getCapability(CapabilityTemplate.TEMPLATE_KEY_CAPABILITY).ifPresent(key ->\n                            provider.requestRemoteUpdate(key, PacketDistributor.PLAYER.with(() -> (ServerPlayer) player)));\n                }\n            });\n        });\n\n        NetworkHooks.openScreen((ServerPlayer) player, (TemplateManagerTileEntity) te, pos);\n        return InteractionResult.SUCCESS;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/capability/CapabilityProviderEnergy.java",
    "content": "package com.direwolf20.buildinggadgets.common.capability;\n\nimport net.minecraft.core.Direction;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraftforge.common.capabilities.Capability;\nimport net.minecraftforge.common.capabilities.ForgeCapabilities;\nimport net.minecraftforge.common.capabilities.ICapabilityProvider;\nimport net.minecraftforge.common.util.LazyOptional;\n\nimport javax.annotation.Nonnull;\nimport javax.annotation.Nullable;\nimport java.util.function.IntSupplier;\n\npublic class CapabilityProviderEnergy implements ICapabilityProvider {\n    private final ItemEnergyForge energyItem;\n    private final LazyOptional<ItemEnergyForge> energyCapability;\n\n    public CapabilityProviderEnergy(ItemStack stack, IntSupplier energyCapacity) {\n        this.energyItem = new ItemEnergyForge(stack,energyCapacity);\n        this.energyCapability = LazyOptional.of(() -> energyItem);\n    }\n\n    @Nonnull\n    @Override\n    public <T> LazyOptional<T> getCapability(@Nonnull Capability<T> cap, @Nullable Direction side) {\n        return cap == ForgeCapabilities.ENERGY ? energyCapability.cast() : LazyOptional.empty();\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/capability/CapabilityTemplate.java",
    "content": "package com.direwolf20.buildinggadgets.common.capability;\n\nimport com.direwolf20.buildinggadgets.common.tainted.template.ITemplateKey;\nimport com.direwolf20.buildinggadgets.common.tainted.template.ITemplateProvider;\nimport net.minecraftforge.common.capabilities.Capability;\nimport net.minecraftforge.common.capabilities.CapabilityManager;\nimport net.minecraftforge.common.capabilities.CapabilityToken;\n\npublic class CapabilityTemplate {\n    public static final Capability<ITemplateProvider> TEMPLATE_PROVIDER_CAPABILITY = CapabilityManager.get(new CapabilityToken<>(){});\n    public static final Capability<ITemplateKey> TEMPLATE_KEY_CAPABILITY = CapabilityManager.get(new CapabilityToken<>(){});\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/capability/ConfigEnergyStorage.java",
    "content": "package com.direwolf20.buildinggadgets.common.capability;\n\nimport com.direwolf20.buildinggadgets.common.tainted.Tainted;\nimport net.minecraftforge.energy.IEnergyStorage;\n\nimport java.util.function.IntSupplier;\n\n@Tainted(reason = \"This shouldn't have have 3 layers\")\npublic abstract class ConfigEnergyStorage implements IEnergyStorage {\n    private final IntSupplier capacitySupplier;\n    private final IntSupplier extractSupplier;\n    private final IntSupplier receiveSupplier;\n    private int energy;\n\n    public ConfigEnergyStorage(IntSupplier capacitySupplier, IntSupplier extractSupplier, IntSupplier receiveSupplier) {\n        this.capacitySupplier = capacitySupplier;\n        this.extractSupplier = extractSupplier;\n        this.receiveSupplier = receiveSupplier;\n        this.energy = 0;\n    }\n\n    public ConfigEnergyStorage(IntSupplier capacitySupplier, IntSupplier transfer) {\n        this(capacitySupplier, transfer, transfer);\n    }\n\n    public ConfigEnergyStorage(IntSupplier capacity) {\n        this(capacity, capacity);\n    }\n\n    @Override\n    public int getEnergyStored() {\n        updateEnergy();\n        return getEnergyStoredCache();\n    }\n\n    @Override\n    public int getMaxEnergyStored() {\n        return capacitySupplier.getAsInt();\n    }\n\n    @Override\n    public int receiveEnergy(int maxReceive, boolean simulate) {\n        if (maxReceive < 0)\n            return 0;\n        int energyReceived = evaluateEnergyReceived(maxReceive, simulate);\n        if (! simulate) {\n            setEnergy(energyReceived + getEnergyStored());\n            writeEnergy();\n        }\n        return energyReceived;\n    }\n\n    @Override\n    public int extractEnergy(int maxExtract, boolean simulate) {\n        if (maxExtract < 0)\n            return 0;\n        int energyExtracted = evaluateEnergyExtracted(maxExtract, simulate);\n        if (! simulate) {\n            setEnergy(getEnergyStored() - energyExtracted);\n            writeEnergy();\n        }\n        return energyExtracted;\n    }\n\n    /**\n     * Returns if this storage can have energy extracted.\n     * If this is false, then any calls to extractEnergy will return 0.\n     */\n    @Override\n    public boolean canExtract() {\n        return extractSupplier.getAsInt() > 0;\n    }\n\n    /**\n     * Used to determine if this storage can receive energy.\n     * If this is false, then any calls to receiveEnergy will return 0.\n     */\n    @Override\n    public boolean canReceive() {\n        return receiveSupplier.getAsInt() > 0;\n    }\n\n    protected IntSupplier getExtractSupplier() {\n        return extractSupplier;\n    }\n\n    protected IntSupplier getReceiveSupplier() {\n        return receiveSupplier;\n    }\n\n    protected int evaluateEnergyExtracted(int maxExtract, boolean simulate) {\n        return Math.min(getEnergyStored(), Math.min(maxExtract, getExtractSupplier().getAsInt()));\n    }\n\n    protected int evaluateEnergyReceived(int maxReceive, boolean simulate) {\n        return Math.min(getMaxEnergyStored() - getEnergyStored(), Math.min(maxReceive, getReceiveSupplier().getAsInt()));\n    }\n\n    public void setEnergy(int energy) {\n        this.energy = energy;\n    }\n\n    protected int getEnergyStoredCache() {\n        return energy;\n    }\n\n    protected abstract void writeEnergy();\n\n    protected abstract void updateEnergy();\n\n    protected void updateMaxEnergy() {\n        this.energy = Math.min(getMaxEnergyStored(), energy);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/capability/IPrivateEnergy.java",
    "content": "package com.direwolf20.buildinggadgets.common.capability;\n\nimport net.minecraftforge.energy.IEnergyStorage;\n\npublic interface IPrivateEnergy extends IEnergyStorage {\n\n    /**\n     * Do not use {@link #extractEnergy(int, boolean)} internally. This method\n     * stops the gadgets from being used like batteries.\n     */\n    int extractPower(int maxExtract, boolean simulate);\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/capability/ItemEnergyForge.java",
    "content": "package com.direwolf20.buildinggadgets.common.capability;\n\nimport com.direwolf20.buildinggadgets.common.util.ref.NBTKeys;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.nbt.CompoundTag;\n\nimport java.util.function.IntSupplier;\n\npublic final class ItemEnergyForge extends ConfigEnergyStorage implements IPrivateEnergy {\n    private final ItemStack stack;\n\n    public ItemEnergyForge(ItemStack stack, IntSupplier capacity) {\n        super(capacity);\n        this.stack = stack;\n    }\n\n    protected void writeEnergy() {\n        CompoundTag nbt = stack.getOrCreateTag();\n        nbt.putInt(NBTKeys.ENERGY, getEnergyStoredCache());\n    }\n\n    protected void updateEnergy() {\n        CompoundTag nbt = stack.getOrCreateTag();\n        if (nbt.contains(NBTKeys.ENERGY))\n            setEnergy(nbt.getInt(NBTKeys.ENERGY));\n        updateMaxEnergy();\n    }\n\n    @Override\n    public int extractEnergy(int maxExtract, boolean simulate) {\n        return 0;\n    }\n\n    /**\n     * Do not use {@link #extractEnergy(int, boolean)} internally. This method\n     * stops the gadgets from being used like batteries.\n     */\n    public int extractPower(int maxExtract, boolean simulate) {\n        if (maxExtract < 0)\n            return 0;\n\n        int energyExtracted = evaluateEnergyExtracted(maxExtract, simulate);\n        if (! simulate) {\n            setEnergy(getEnergyStored() - energyExtracted);\n            writeEnergy();\n        }\n        return energyExtracted;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/capability/ItemTemplateKey.java",
    "content": "package com.direwolf20.buildinggadgets.common.capability;\n\nimport com.direwolf20.buildinggadgets.common.tainted.template.ITemplateKey;\nimport com.direwolf20.buildinggadgets.common.util.ref.NBTKeys;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.nbt.CompoundTag;\n\nimport java.util.UUID;\nimport java.util.function.Supplier;\n\npublic final class ItemTemplateKey implements ITemplateKey {\n    private final ItemStack stack;\n\n    public ItemTemplateKey(ItemStack stack) {\n        this.stack = stack;\n    }\n\n    @Override\n    public UUID getTemplateId(Supplier<UUID> freeIdAllocator) {\n        CompoundTag nbt = stack.getOrCreateTag();\n        if (! nbt.hasUUID(NBTKeys.TEMPLATE_KEY_ID)) {\n            UUID newID = freeIdAllocator.get();\n            nbt.putUUID(NBTKeys.TEMPLATE_KEY_ID, newID);\n            return newID;\n        }\n        return nbt.getUUID(NBTKeys.TEMPLATE_KEY_ID);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/capability/PasteContainerCapabilityProvider.java",
    "content": "package com.direwolf20.buildinggadgets.common.capability;\n\nimport net.minecraft.core.Direction;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraftforge.common.capabilities.Capability;\nimport net.minecraftforge.common.capabilities.ForgeCapabilities;\nimport net.minecraftforge.common.capabilities.ICapabilityProvider;\nimport net.minecraftforge.common.util.LazyOptional;\n\nimport javax.annotation.Nonnull;\nimport javax.annotation.Nullable;\n\npublic class PasteContainerCapabilityProvider implements ICapabilityProvider {\n    private final LazyOptional<PasteContainerItemHandler> itemHandler;\n\n    public PasteContainerCapabilityProvider(ItemStack container) {\n        this.itemHandler = LazyOptional.of(() -> new PasteContainerItemHandler(container));\n    }\n\n    @Nonnull\n    @Override\n    public <T> LazyOptional<T> getCapability(@Nonnull Capability<T> cap, @Nullable Direction side) {\n        if (ForgeCapabilities.ITEM_HANDLER == cap)\n            return itemHandler.cast();\n        return LazyOptional.empty();\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/capability/PasteContainerItemHandler.java",
    "content": "package com.direwolf20.buildinggadgets.common.capability;\n\nimport com.direwolf20.buildinggadgets.common.items.ConstructionPasteContainer;\nimport com.direwolf20.buildinggadgets.common.items.OurItems;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.util.Mth;\nimport net.minecraftforge.items.IItemHandlerModifiable;\n\nimport javax.annotation.Nonnull;\n\npublic final class PasteContainerItemHandler implements IItemHandlerModifiable {\n    private final ItemStack container;\n    private final boolean isCreative;\n\n    public PasteContainerItemHandler(ItemStack container) {\n        this.container = container;\n        this.isCreative = getContainerItem().isCreative();\n    }\n\n    @Override\n    public void setStackInSlot(int slot, @Nonnull ItemStack stack) {\n        setCount(stack.getCount(), false);\n    }\n\n    @Override\n    public int getSlots() {\n        return 1;\n    }\n\n    @Nonnull\n    @Override\n    public ItemStack getStackInSlot(int slot) {\n        int count = getCount();\n        return count <= 0 ? ItemStack.EMPTY : new ItemStack(OurItems.CONSTRUCTION_PASTE_ITEM.get(), count);\n    }\n\n    @Nonnull\n    @Override\n    public ItemStack insertItem(int slot, @Nonnull ItemStack stack, boolean simulate) {\n        if (stack.isEmpty() || ! isItemValid(slot, stack))\n            return stack;\n        if (isCreative)\n            return ItemStack.EMPTY;\n        int currentCount = getCount();\n        int newCount = setCount(currentCount + stack.getCount(), simulate);\n        int dif = newCount - currentCount;\n        if (dif == 0)\n            return stack;\n        stack = stack.copy();\n        stack.setCount(stack.getCount() - dif);\n        return stack;\n    }\n\n    @Nonnull\n    @Override\n    public ItemStack extractItem(int slot, int amount, boolean simulate) {\n        if (amount <= 0)\n            return ItemStack.EMPTY;\n\n        if (isCreative)\n            return new ItemStack(OurItems.CONSTRUCTION_PASTE_ITEM.get(), amount);\n\n        int currentCount = getCount();\n        int newCount = setCount(currentCount - amount, simulate);\n\n        int dif = currentCount - newCount;\n        if (dif == 0)\n            return ItemStack.EMPTY;\n\n        return new ItemStack(OurItems.CONSTRUCTION_PASTE_ITEM.get(), dif);\n    }\n\n    @Override\n    public int getSlotLimit(int slot) {\n        return getContainerItem().getMaxCapacity();\n    }\n\n    @Override\n    public boolean isItemValid(int slot, @Nonnull ItemStack stack) {\n        return stack.getItem() == OurItems.CONSTRUCTION_PASTE_ITEM.get();\n    }\n\n    private int getCount() {\n        return getContainerItem().getPasteCount(container);\n    }\n\n    private ConstructionPasteContainer getContainerItem() {\n        return ((ConstructionPasteContainer) container.getItem());\n    }\n\n    private int setCount(int count, boolean simulate) {\n        int res = Mth.clamp(count, 0, getContainerItem().getMaxCapacity());\n        if (! simulate)\n            getContainerItem().setPasteCount(container, res);\n        return res;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/capability/provider/MultiCapabilityProvider.java",
    "content": "package com.direwolf20.buildinggadgets.common.capability.provider;\n\nimport com.direwolf20.buildinggadgets.common.tainted.Tainted;\nimport com.google.common.collect.ImmutableList;\nimport net.minecraft.core.Direction;\nimport net.minecraftforge.common.capabilities.Capability;\nimport net.minecraftforge.common.capabilities.ICapabilityProvider;\nimport net.minecraftforge.common.util.LazyOptional;\n\nimport javax.annotation.Nonnull;\nimport javax.annotation.Nullable;\n\n/**\n * Universal capability provider that links multiple capability provider to share all of their properties.\n * <p>This means the order of providers matters, where it determines the first capability you will get if multiple child providers works some set of parameters.</p>\n */\n@Tainted(reason = \"Not required in any way\")\npublic class MultiCapabilityProvider implements ICapabilityProvider {\n\n    private final ImmutableList<ICapabilityProvider> childProviders;\n\n    public MultiCapabilityProvider(ICapabilityProvider... childProviders) {\n        this(ImmutableList.copyOf(childProviders));\n    }\n\n    public MultiCapabilityProvider(ImmutableList<ICapabilityProvider> childProviders) {\n        this.childProviders = childProviders;\n    }\n\n    @Nonnull\n    @Override\n    public <T> LazyOptional<T> getCapability(@Nonnull Capability<T> cap, @Nullable Direction side) {\n        for (ICapabilityProvider provider : childProviders) {\n            LazyOptional<T> optional = provider.getCapability(cap, side);\n            if (optional.isPresent()) return optional;\n        }\n        return LazyOptional.empty();\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/capability/provider/TemplateKeyProvider.java",
    "content": "package com.direwolf20.buildinggadgets.common.capability.provider;\n\nimport com.direwolf20.buildinggadgets.common.capability.CapabilityTemplate;\nimport com.direwolf20.buildinggadgets.common.capability.ItemTemplateKey;\nimport com.direwolf20.buildinggadgets.common.tainted.template.ITemplateKey;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.core.Direction;\nimport net.minecraftforge.common.capabilities.Capability;\nimport net.minecraftforge.common.capabilities.ICapabilityProvider;\nimport net.minecraftforge.common.util.LazyOptional;\n\nimport javax.annotation.Nonnull;\nimport javax.annotation.Nullable;\n\npublic final class TemplateKeyProvider implements ICapabilityProvider {\n    private final LazyOptional<ITemplateKey> lazyOpt;\n\n    public TemplateKeyProvider(ItemStack stack) {\n        ItemTemplateKey key = new ItemTemplateKey(stack);\n        lazyOpt = LazyOptional.of(() -> key);\n    }\n\n    @Nonnull\n    @Override\n    public <T> LazyOptional<T> getCapability(@Nonnull Capability<T> cap, @Nullable Direction side) {\n        if (cap == CapabilityTemplate.TEMPLATE_KEY_CAPABILITY)\n            return lazyOpt.cast();\n        return LazyOptional.empty();\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/capability/provider/TemplateProviderCapabilityProvider.java",
    "content": "package com.direwolf20.buildinggadgets.common.capability.provider;\n\nimport com.direwolf20.buildinggadgets.common.capability.CapabilityTemplate;\nimport com.direwolf20.buildinggadgets.common.tainted.template.ITemplateProvider;\nimport net.minecraft.core.Direction;\nimport net.minecraftforge.common.capabilities.Capability;\nimport net.minecraftforge.common.capabilities.ICapabilityProvider;\nimport net.minecraftforge.common.util.LazyOptional;\n\nimport javax.annotation.Nonnull;\nimport javax.annotation.Nullable;\n\npublic final class TemplateProviderCapabilityProvider implements ICapabilityProvider {\n    private final LazyOptional<ITemplateProvider> opt;\n\n    public TemplateProviderCapabilityProvider(ITemplateProvider provider) {\n        this.opt = LazyOptional.of(() -> provider);\n    }\n\n    @Nonnull\n    @Override\n    public <T> LazyOptional<T> getCapability(@Nonnull Capability<T> cap, @Nullable Direction side) {\n        if (cap == CapabilityTemplate.TEMPLATE_PROVIDER_CAPABILITY)\n            return opt.cast();\n        return LazyOptional.empty();\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/commands/AllowPlayerOverrideManager.java",
    "content": "package com.direwolf20.buildinggadgets.common.commands;\n\nimport com.direwolf20.buildinggadgets.common.BuildingGadgets;\nimport com.direwolf20.buildinggadgets.common.util.lang.ITranslationProvider;\nimport com.direwolf20.buildinggadgets.common.util.lang.Styles;\nimport com.google.common.cache.Cache;\nimport com.google.common.cache.CacheBuilder;\nimport com.google.common.cache.RemovalListener;\nimport com.mojang.brigadier.context.CommandContext;\nimport net.minecraft.commands.CommandSourceStack;\nimport net.minecraft.network.chat.MutableComponent;\nimport net.minecraft.world.entity.player.Player;\n\nimport java.util.Map;\nimport java.util.UUID;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.TimeUnit;\n\nfinal class AllowPlayerOverrideManager {\n    private final Cache<UUID, Boolean> allowPlayerOverrideCache;\n    private final ITranslationProvider noPlayerTranslation;\n    private final ITranslationProvider toggledTranslation;\n    private final ITranslationProvider listTranslation;\n    private final String logMessage;\n\n    public AllowPlayerOverrideManager(ITranslationProvider noPlayerTranslation, ITranslationProvider toggledTranslation, ITranslationProvider listTranslation, String logMessage) {\n        allowPlayerOverrideCache = CacheBuilder.newBuilder()\n                .expireAfterWrite(5, TimeUnit.MINUTES)\n                .removalListener((RemovalListener<UUID, Boolean>) notification -> BuildingGadgets.LOG.info(\n                        \"Player with id {} was removed from the list of players for which '{}' is {} enabled. \" +\n                                \"He/She will need to run the command again for '{}' to become active again.\",\n                        notification.getKey(), logMessage, notification.getValue() ? \"\" : \"not\", logMessage))\n                .build();\n\n        this.noPlayerTranslation = noPlayerTranslation;\n        this.toggledTranslation = toggledTranslation;\n        this.listTranslation = listTranslation;\n        this.logMessage = logMessage;\n    }\n\n    void toggleAllowOverride(Player player) {\n        toggleAllowOverride(player.getUUID());\n    }\n\n    void toggleAllowOverride(UUID uuid) {\n        try {\n            allowPlayerOverrideCache.put(uuid, !allowPlayerOverrideCache.get(uuid, () -> false));\n        } catch (ExecutionException e) {\n            BuildingGadgets.LOG.warn(\"Failed to toggle '{}' for player {}\", logMessage, uuid, e);\n        }\n    }\n\n    boolean mayOverride(UUID uuid) {\n        Boolean res = allowPlayerOverrideCache.getIfPresent(uuid);\n        return res != null ? res : false;\n    }\n\n    boolean mayOverride(Player player) {\n        return mayOverride(player.getUUID());\n    }\n\n    int executeToggle(CommandContext<CommandSourceStack> context, Player player) {\n        if (player == null) {\n            context.getSource().sendFailure(noPlayerTranslation.componentTranslation().setStyle(Styles.RED));\n            return 0;\n        }\n        toggleAllowOverride(player);\n        context.getSource().sendSuccess(toggledTranslation.componentTranslation(player.getDisplayName(), mayOverride(player))\n                .setStyle(Styles.AQUA), true);\n        return 1;\n    }\n\n    int executeList(CommandContext<CommandSourceStack> context) {\n        for (Map.Entry<UUID, Boolean> entry : allowPlayerOverrideCache.asMap().entrySet()) {\n            MutableComponent component = listTranslation.componentTranslation(entry.getKey(), entry.getValue());\n            component = entry.getValue() ? component.setStyle(Styles.BLUE) : component.setStyle(Styles.DK_GREEN);\n            context.getSource().sendSuccess(component, true);\n        }\n        return 1;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/commands/ForceUnloadedCommand.java",
    "content": "package com.direwolf20.buildinggadgets.common.commands;\n\nimport com.direwolf20.buildinggadgets.common.util.lang.CommandTranslation;\nimport com.mojang.brigadier.builder.LiteralArgumentBuilder;\nimport com.mojang.brigadier.context.CommandContext;\nimport net.minecraft.commands.CommandSourceStack;\nimport net.minecraft.commands.Commands;\nimport net.minecraft.commands.arguments.EntityArgument;\nimport net.minecraft.world.entity.player.Player;\n\nimport java.util.UUID;\n\npublic class ForceUnloadedCommand {\n    private static final AllowPlayerOverrideManager ALLOW_UNLOADED_CHUNKS = new AllowPlayerOverrideManager(\n            CommandTranslation.FORCE_UNLOADED_NO_PLAYER, CommandTranslation.FORCE_UNLOADED_TOGGLED,\n            CommandTranslation.FORCE_UNLOADED_LIST, \"allow unloaded chunks\"\n    );\n\n    public static void toggleAllowUnloadedChunks(Player player) {\n        ALLOW_UNLOADED_CHUNKS.toggleAllowOverride(player);\n    }\n\n    public static void toggleAllowUnloadedChunks(UUID uuid) {\n        ALLOW_UNLOADED_CHUNKS.toggleAllowOverride(uuid);\n    }\n\n    public static boolean mayForceUnloadedChunks(UUID uuid) {\n        return ALLOW_UNLOADED_CHUNKS.mayOverride(uuid);\n    }\n\n    public static boolean mayForceUnloadedChunks(Player player) {\n        return ALLOW_UNLOADED_CHUNKS.mayOverride(player);\n    }\n\n    public static LiteralArgumentBuilder<CommandSourceStack> registerToggle() {\n        return Commands.literal(\"ForceLoadChunks\")\n                .requires(commandSource -> commandSource.hasPermission(2))\n                .then(Commands.argument(\"player\", EntityArgument.player())\n                        .executes(context -> executeToggle(context, EntityArgument.getPlayer(context, \"player\")))\n                );\n    }\n\n    public static LiteralArgumentBuilder<CommandSourceStack> registerList() {\n        return Commands.literal(\"ForceLoadChunksList\")\n                .requires(commandSource -> commandSource.hasPermission(2))\n                .executes(ForceUnloadedCommand::executeList);\n    }\n\n    private static int executeToggle(CommandContext<CommandSourceStack> context, Player player) {\n        return ALLOW_UNLOADED_CHUNKS.executeToggle(context, player);\n    }\n\n    private static int executeList(CommandContext<CommandSourceStack> context) {\n        return ALLOW_UNLOADED_CHUNKS.executeList(context);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/commands/OverrideBuildSizeCommand.java",
    "content": "package com.direwolf20.buildinggadgets.common.commands;\n\nimport com.direwolf20.buildinggadgets.common.util.lang.CommandTranslation;\nimport com.mojang.brigadier.builder.LiteralArgumentBuilder;\nimport com.mojang.brigadier.context.CommandContext;\nimport net.minecraft.commands.CommandSourceStack;\nimport net.minecraft.commands.Commands;\nimport net.minecraft.commands.arguments.EntityArgument;\nimport net.minecraft.world.entity.player.Player;\n\nimport java.util.UUID;\n\npublic final class OverrideBuildSizeCommand {\n    private static final AllowPlayerOverrideManager ALLOW_LARGE_BUILDS = new AllowPlayerOverrideManager(\n            CommandTranslation.OVERRIDE_BUILD_SIZE_NO_PLAYER, CommandTranslation.OVERRIDE_BUILD_SIZE_TOGGLED,\n            CommandTranslation.OVERRIDE_BUILD_SIZE_LIST, \"override build size\"\n    );\n\n    public static void toggleAllowLargeBuilds(Player player) {\n        ALLOW_LARGE_BUILDS.toggleAllowOverride(player);\n    }\n\n    public static void toggleAllowLargeBuilds(UUID uuid) {\n        ALLOW_LARGE_BUILDS.toggleAllowOverride(uuid);\n    }\n\n    public static boolean mayPerformLargeBuild(UUID uuid) {\n        return ALLOW_LARGE_BUILDS.mayOverride(uuid);\n    }\n\n    public static boolean mayPerformLargeBuild(Player player) {\n        return ALLOW_LARGE_BUILDS.mayOverride(player);\n    }\n\n    public static LiteralArgumentBuilder<CommandSourceStack> registerToggle() {\n        return Commands.literal(\"OverrideBuildSize\")\n                .requires(commandSource -> commandSource.hasPermission(2))\n                .then(Commands.argument(\"player\", EntityArgument.player())\n                        .executes(context -> executeToggle(context, EntityArgument.getPlayer(context, \"player\")))\n                );\n    }\n\n    public static LiteralArgumentBuilder<CommandSourceStack> registerList() {\n        return Commands.literal(\"OverrideBuildSizeList\")\n                .requires(commandSource -> commandSource.hasPermission(2))\n                .executes(OverrideBuildSizeCommand::executeList);\n    }\n\n    private static int executeToggle(CommandContext<CommandSourceStack> context, Player player) {\n        return ALLOW_LARGE_BUILDS.executeToggle(context, player);\n    }\n\n    private static int executeList(CommandContext<CommandSourceStack> context) {\n        return ALLOW_LARGE_BUILDS.executeList(context);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/commands/OverrideCopySizeCommand.java",
    "content": "package com.direwolf20.buildinggadgets.common.commands;\n\nimport com.direwolf20.buildinggadgets.common.util.lang.CommandTranslation;\nimport com.mojang.brigadier.builder.LiteralArgumentBuilder;\nimport com.mojang.brigadier.context.CommandContext;\nimport net.minecraft.commands.CommandSourceStack;\nimport net.minecraft.commands.Commands;\nimport net.minecraft.commands.arguments.EntityArgument;\nimport net.minecraft.world.entity.player.Player;\n\nimport java.util.UUID;\n\npublic final class OverrideCopySizeCommand {\n    private static final AllowPlayerOverrideManager ALLOW_LARGE_COPIES = new AllowPlayerOverrideManager(\n            CommandTranslation.OVERRIDE_COPY_SIZE_NO_PLAYER, CommandTranslation.OVERRIDE_COPY_SIZE_TOGGLED,\n            CommandTranslation.OVERRIDE_COPY_SIZE_LIST, \"override copy size\"\n    );\n\n    public static void toggleAllowLargeCopies(Player player) {\n        ALLOW_LARGE_COPIES.toggleAllowOverride(player);\n    }\n\n    public static void toggleAllowLargeCopies(UUID uuid) {\n        ALLOW_LARGE_COPIES.toggleAllowOverride(uuid);\n    }\n\n    public static boolean mayPerformLargeCopy(UUID uuid) {\n        return ALLOW_LARGE_COPIES.mayOverride(uuid);\n    }\n\n    public static boolean mayPerformLargeCopy(Player player) {\n        return ALLOW_LARGE_COPIES.mayOverride(player);\n    }\n\n    public static LiteralArgumentBuilder<CommandSourceStack> registerToggle() {\n        return Commands.literal(\"OverrideCopySize\")\n                .requires(commandSource -> commandSource.hasPermission(2))\n                .then(Commands.argument(\"player\", EntityArgument.player())\n                        .executes(context -> executeToggle(context, EntityArgument.getPlayer(context, \"player\")))\n                );\n    }\n\n    public static LiteralArgumentBuilder<CommandSourceStack> registerList() {\n        return Commands.literal(\"OverrideCopySizeList\")\n                .requires(commandSource -> commandSource.hasPermission(2))\n                .executes(OverrideCopySizeCommand::executeList);\n    }\n\n    private static int executeToggle(CommandContext<CommandSourceStack> context, Player player) {\n        return ALLOW_LARGE_COPIES.executeToggle(context, player);\n    }\n\n    private static int executeList(CommandContext<CommandSourceStack> context) {\n        return ALLOW_LARGE_COPIES.executeList(context);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/config/Config.java",
    "content": "package com.direwolf20.buildinggadgets.common.config;\n\nimport net.minecraftforge.common.ForgeConfigSpec;\nimport net.minecraftforge.fml.common.Mod.EventBusSubscriber;\n\nimport static net.minecraftforge.common.ForgeConfigSpec.*;\n\nimport net.minecraftforge.common.ForgeConfigSpec.BooleanValue;\nimport net.minecraftforge.common.ForgeConfigSpec.Builder;\nimport net.minecraftforge.common.ForgeConfigSpec.DoubleValue;\nimport net.minecraftforge.common.ForgeConfigSpec.IntValue;\n\n@EventBusSubscriber\npublic class Config {\n    private static final Builder SERVER_BUILDER = new Builder();\n    private static final Builder CLIENT_BUILDER = new Builder();\n\n    public static final CategoryGeneral GENERAL = new CategoryGeneral();\n    public static final CategoryGadgets GADGETS = new CategoryGadgets();\n    public static final CategoryPasteContainers PASTE_CONTAINERS = new CategoryPasteContainers();\n\n    public static final class CategoryGeneral {\n\n        public final DoubleValue rayTraceRange;\n        public final BooleanValue allowAbsoluteCoords;\n        /* Client Only!*/\n        public final BooleanValue absoluteCoordDefault;\n        public final BooleanValue allowOverwriteBlocks;\n\n        private CategoryGeneral() {\n            SERVER_BUILDER.comment(\"General mod settings\").push(\"general\");\n            CLIENT_BUILDER.comment(\"General mod settings\").push(\"general\");\n\n            allowAbsoluteCoords = SERVER_BUILDER\n                    .comment(\"Defined whether or not a player can use Absolute Coords mode in the Copy Paste Gadget\")\n                    .define(\"Allow Absolute Coords\", true);\n\n            rayTraceRange = SERVER_BUILDER\n                    .comment(\"Defines how far away you can build\")\n                    .defineInRange(\"MaxBuildDistance\", 32D, 1, 48);\n\n            absoluteCoordDefault = CLIENT_BUILDER\n                    .comment(\"Determines if the Copy/Paste GUI's coordinate mode starts in 'Absolute' mode by default.\", \"Set to true for Absolute, set to False for Relative.\")\n                    .define(\"Default to absolute Coord-Mode\", false);\n\n            allowOverwriteBlocks = SERVER_BUILDER\n                    .comment(\"Whether the Building / CopyPaste Gadget can overwrite blocks like water, lava, grass, etc (like a player can).\",\n                            \"False will only allow it to overwrite air blocks.\")\n                    .define(\"Allow non-Air-Block-Overwrite\", true);\n\n            CLIENT_BUILDER.pop();\n            SERVER_BUILDER.pop();\n        }\n    }\n\n    public static final class CategoryGadgets {\n        public final IntValue maxRange;\n        public final IntValue placeSteps;\n\n        public final GadgetConfig GADGET_BUILDING;\n        public final GadgetConfig GADGET_EXCHANGER;\n        public final CategoryGadgetDestruction GADGET_DESTRUCTION;\n        public final CategoryGadgetCopyPaste GADGET_COPY_PASTE;\n\n        private CategoryGadgets() {\n            SERVER_BUILDER.comment(\"Configure the Gadgets\").push(\"Gadgets\");\n            maxRange = SERVER_BUILDER\n                    .comment(\"The max range of the Gadgets\")\n                    .defineInRange(\"Maximum allowed Range\", 15, 1, 32);\n\n            //use the old cap as the synchronous border... This implies that 32*32*32 areas are the max size for a synchronous copy by default\n            placeSteps = SERVER_BUILDER\n                    .comment(\"Maximum amount of Blocks to be placed in one Tick.\",\n                            \"Notice that an EffectBlock takes 20 ticks to place, therefore a Server has to handle 20-times this value effect-block Tile's at once. \" +\n                            \"Reduce this if  you notice lag-spikes from Players placing Templates.\",\n                            \"Of course decreasing this value will result in more time required to place large TemplateItem's.\")\n                    .defineInRange(\"Max Placement/Tick\", 1024, 1, Integer.MAX_VALUE);\n\n            GADGET_BUILDING = new GadgetConfig(\"Building Gadget\", 500000, 50, 10);\n            GADGET_EXCHANGER = new GadgetConfig(\"Exchanging Gadget\", 500000, 100, 10);\n            GADGET_DESTRUCTION = new CategoryGadgetDestruction();\n            GADGET_COPY_PASTE = new CategoryGadgetCopyPaste();\n\n            SERVER_BUILDER.pop();\n        }\n\n        public static class GadgetConfig {\n            public final IntValue maxEnergy;\n            public final IntValue energyCost;\n            public final IntValue undoSize;\n\n            public GadgetConfig(String name, int maxEnergy, int energyCost, int getMaxUndo) {\n                SERVER_BUILDER.comment(\"Energy Cost & Durability of the \" + name).push(name);\n\n                this.maxEnergy = SERVER_BUILDER\n                        .comment(\"The max energy of the Gadget, set to 0 to disable energy usage\")\n                        .defineInRange(\"Maximum Energy\", maxEnergy, 0, Integer.MAX_VALUE);\n\n                this.energyCost = SERVER_BUILDER\n                        .comment(\"The Gadget's Energy cost per Operation\")\n                        .defineInRange(\"Energy Cost\", energyCost, 0, Integer.MAX_VALUE);\n\n                this.undoSize = SERVER_BUILDER\n                        .comment(\"The Gadget's Max Undo size (Note, the exchanger does not support undo)\")\n                        .defineInRange(\"Max Undo History Size\", getMaxUndo, 0, 128);\n\n                SERVER_BUILDER.pop();\n            }\n        }\n\n        public static final class CategoryGadgetDestruction extends GadgetConfig {\n            public final IntValue destroySize;\n            public final DoubleValue nonFuzzyMultiplier;\n            public final BooleanValue nonFuzzyEnabled;\n\n            private CategoryGadgetDestruction() {\n                super(\"Destruction Gadget\", 1000000, 200, 1);\n\n                SERVER_BUILDER\n                        .comment(\"Energy Cost, Durability & Maximum Energy of the Destruction Gadget\")\n                        .push(\"Destruction Gadget\");\n\n\n                destroySize = SERVER_BUILDER\n                        .comment(\"The maximum dimensions, the Destruction Gadget can destroy.\")\n                        .defineInRange(\"Destroy Dimensions\", 16, 0, 32);\n\n                nonFuzzyMultiplier = SERVER_BUILDER\n                        .comment(\"The cost in energy/durability will increase by this amount when not in fuzzy mode\")\n                        .defineInRange(\"Non-Fuzzy Mode Multiplier\", 2, 0, Double.MAX_VALUE);\n\n                nonFuzzyEnabled = SERVER_BUILDER\n                        .comment(\"If enabled, the Destruction Gadget can be taken out of fuzzy mode, allowing only instances of the block \"\n                                + \"clicked to be removed (at a higher cost)\")\n                        .define(\"Non-Fuzzy Mode Enabled\", false);\n\n                SERVER_BUILDER.pop();\n\n            }\n        }\n\n        public static final class CategoryGadgetCopyPaste extends GadgetConfig {\n            public final IntValue copySteps;\n            public final IntValue maxCopySize;\n            public final IntValue maxBuildSize;\n\n            private CategoryGadgetCopyPaste() {\n                super(\"Copy-Paste Gadget\", 500000, 50, 1);\n\n                SERVER_BUILDER\n                        .comment(\"Energy Cost & Durability of the Copy-Paste Gadget\")\n                        .push(\"Copy-Paste Gadget\");\n\n                //use the old cap as the per tick border... This implies that 32*32*32 areas are the max size for a one tick copy by default\n                copySteps = SERVER_BUILDER\n                        .comment(\"Maximum amount of Blocks to be copied in one Tick. \",\n                                \"Lower values may improve Server-Performance when copying large Templates\")\n                        .defineInRange(\"Max Copy/Tick\", 32768, 1, Integer.MAX_VALUE);\n\n                maxCopySize = SERVER_BUILDER\n                        .comment(\"Maximum dimensions (x, y and z) that can be copied by a Template without requiring special permission.\",\n                                \"Permission can be granted using the '/buildinggadgets OverrideCopySize [<Player>]' command.\")\n                        .defineInRange(\"Max Copy Dimensions\", 256, - 1, Integer.MAX_VALUE);\n\n                maxBuildSize = SERVER_BUILDER\n                        .comment(\"Maximum dimensions (x, y and z) that can be build by a Template without requiring special permission.\",\n                                \"Permission can be granted using the '/buildinggadgets OverrideBuildSize [<Player>]' command.\")\n                        .defineInRange(\"Max Build Dimensions\", 256, - 1, Integer.MAX_VALUE);\n\n                SERVER_BUILDER.pop();\n            }\n        }\n    }\n\n    public static final class CategoryPasteContainers {\n\n        public final IntValue capacityT1, capacityT2, capacityT3;\n\n        private CategoryPasteContainers() {\n            SERVER_BUILDER\n                    .comment(\"Configure the Paste Containers\")\n                    .push(\"Paste Containers\");\n\n            capacityT1 = getMaxCapacity(1);\n            capacityT2 = getMaxCapacity(2);\n            capacityT3 = getMaxCapacity(3);\n\n            SERVER_BUILDER.pop();\n        }\n\n        private static IntValue getMaxCapacity(int tier) {\n            return SERVER_BUILDER\n                    .comment(String.format(\"The maximum capacity of a tier %s (iron) Construction Paste Container\", tier))\n                    .defineInRange(String.format(\"T%s Container Capacity\", tier), (int) (512 * Math.pow(4, tier - 1)), 1, Integer.MAX_VALUE);\n        }\n    }\n\n    public static final ForgeConfigSpec SERVER_CONFIG = SERVER_BUILDER.build();\n    public static final ForgeConfigSpec CLIENT_CONFIG = CLIENT_BUILDER.build();\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/config/RecipeConstructionPaste.java",
    "content": "package com.direwolf20.buildinggadgets.common.config;\n\nimport com.direwolf20.buildinggadgets.common.items.ConstructionPasteContainer;\nimport com.google.gson.JsonObject;\nimport net.minecraft.core.RegistryAccess;\nimport net.minecraft.world.inventory.CraftingContainer;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.crafting.CraftingBookCategory;\nimport net.minecraft.world.item.crafting.RecipeSerializer;\nimport net.minecraft.world.item.crafting.Ingredient;\nimport net.minecraft.world.item.crafting.ShapedRecipe;\nimport net.minecraft.core.NonNullList;\nimport net.minecraft.resources.ResourceLocation;\n\npublic class RecipeConstructionPaste extends ShapedRecipe {\n\n    public RecipeConstructionPaste(ResourceLocation id, String group, CraftingBookCategory category, int recipeWidth,\n                                   int recipeHeight, NonNullList<Ingredient> recipeItems, ItemStack recipeOutput) {\n        super(id, group, category, recipeWidth, recipeHeight, recipeItems, recipeOutput);\n    }\n\n    @Override\n    public ItemStack assemble(CraftingContainer craftingInventory, RegistryAccess registryAccess) {\n        final ItemStack output = super.assemble(craftingInventory, registryAccess); // Get the default output\n        if (!output.isEmpty()) {\n            int totalPaste = 0;\n            for (int i = 0; i < craftingInventory.getContainerSize(); i++) { // For each slot in the crafting inventory,\n                final ItemStack ingredient = craftingInventory.getItem(i); // Get the ingredient in the slot\n                if (ingredient.getItem() instanceof ConstructionPasteContainer) // If it's a Paste Container,\n                    totalPaste += ConstructionPasteContainer.getPasteAmount(ingredient);\n            }\n            ConstructionPasteContainer.setPasteAmount(output, totalPaste);\n        }\n        return output; // Return the modified output\n    }\n\n    @Override\n    public RecipeSerializer<?> getSerializer() {\n        return Serializer.INSTANCE;\n    }\n\n    public static final class Serializer extends ShapedRecipe.Serializer {\n        public static final Serializer INSTANCE = new Serializer();\n\n        @Override\n        public ShapedRecipe fromJson(ResourceLocation recipeId, JsonObject json) {\n            ShapedRecipe recipe = super.fromJson(recipeId, json);\n            return new RecipeConstructionPaste(recipe.getId(), recipe.getGroup(), CraftingBookCategory.MISC, recipe.getRecipeWidth(),\n                    recipe.getRecipeHeight(), recipe.getIngredients(), recipe.getResultItem(RegistryAccess.EMPTY));\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/containers/BaseContainer.java",
    "content": "package com.direwolf20.buildinggadgets.common.containers;\n\nimport net.minecraft.world.entity.player.Inventory;\nimport net.minecraft.world.inventory.AbstractContainerMenu;\nimport net.minecraft.world.inventory.MenuType;\nimport net.minecraft.world.inventory.Slot;\n\nimport javax.annotation.Nullable;\n\npublic abstract class BaseContainer extends AbstractContainerMenu {\n    public BaseContainer(@Nullable MenuType<?> p_i50105_1_, int p_i50105_2_) {\n        super(p_i50105_1_, p_i50105_2_);\n    }\n\n    protected void addPlayerSlots(Inventory playerInventory, int inX, int inY) {\n        // Slots for the hotbar\n        for (int row = 0; row < 9; ++ row) {\n            int x = inX + row * 18;\n            int y = inY + 86;\n            addSlot(new Slot(playerInventory, row, x, y));\n        }\n        // Slots for the main inventory\n        for (int row = 1; row < 4; ++ row) {\n            for (int col = 0; col < 9; ++ col) {\n                int x = inX + col * 18;\n                int y = row * 18 + (inY + 10);\n                addSlot(new Slot(playerInventory, col + row * 9, x, y));\n            }\n        }\n    }\n\n    protected void addPlayerSlots(Inventory playerInventory) {\n        addPlayerSlots(playerInventory, 8, 56);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/containers/OurContainers.java",
    "content": "package com.direwolf20.buildinggadgets.common.containers;\n\nimport com.direwolf20.buildinggadgets.common.util.ref.Reference;\nimport net.minecraft.world.inventory.MenuType;\nimport net.minecraftforge.common.extensions.IForgeMenuType;\nimport net.minecraftforge.registries.DeferredRegister;\nimport net.minecraftforge.registries.ForgeRegistries;\nimport net.minecraftforge.registries.RegistryObject;\n\npublic final class OurContainers {\n    public static final DeferredRegister<MenuType<?>> CONTAINERS = DeferredRegister.create(ForgeRegistries.MENU_TYPES, Reference.MODID);\n\n    public static final RegistryObject<MenuType<TemplateManagerContainer>> TEMPLATE_MANAGER_CONTAINER\n            = CONTAINERS.register(\"template_manager_container\", () -> IForgeMenuType.create(TemplateManagerContainer::new));\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/containers/TemplateManagerContainer.java",
    "content": "package com.direwolf20.buildinggadgets.common.containers;\n\nimport com.direwolf20.buildinggadgets.common.tileentities.TemplateManagerTileEntity;\nimport com.direwolf20.buildinggadgets.common.util.exceptions.CapabilityNotPresentException;\nimport com.direwolf20.buildinggadgets.common.util.ref.Reference;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.network.FriendlyByteBuf;\nimport net.minecraft.resources.ResourceLocation;\nimport net.minecraft.world.entity.player.Inventory;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.inventory.InventoryMenu;\nimport net.minecraft.world.inventory.Slot;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraftforge.common.capabilities.ForgeCapabilities;\nimport net.minecraftforge.items.IItemHandler;\nimport net.minecraftforge.items.SlotItemHandler;\n\nimport javax.annotation.Nonnull;\nimport java.util.Objects;\n\npublic class TemplateManagerContainer extends BaseContainer {\n    public static final String TEXTURE_LOC_SLOT_TOOL = Reference.MODID + \":item/slot_copy_paste_gadget\";\n    public static final String TEXTURE_LOC_SLOT_TEMPLATE = Reference.MODID + \":item/slot_template\";\n\n    private TemplateManagerTileEntity te;\n\n    public TemplateManagerContainer(int windowId, Inventory playerInventory, FriendlyByteBuf extraData) {\n        super(OurContainers.TEMPLATE_MANAGER_CONTAINER.get(), windowId);\n        BlockPos pos = extraData.readBlockPos();\n\n        this.te = (TemplateManagerTileEntity) playerInventory.player.level.getBlockEntity(pos);\n        addOwnSlots();\n        addPlayerSlots(playerInventory, -12, 70);\n    }\n\n    public TemplateManagerContainer(int windowId, Inventory playerInventory, TemplateManagerTileEntity tileEntity) {\n        super(OurContainers.TEMPLATE_MANAGER_CONTAINER.get(), windowId);\n        this.te = Objects.requireNonNull(tileEntity);\n\n        addOwnSlots();\n        addPlayerSlots(playerInventory, -12, 70);\n    }\n\n    @Override\n    public boolean stillValid(Player playerIn) {\n        return getTe().canInteractWith(playerIn);\n    }\n\n    private void addOwnSlots() {\n        IItemHandler itemHandler = this.getTe().getCapability(ForgeCapabilities.ITEM_HANDLER).orElseThrow(CapabilityNotPresentException::new);\n        int x = 132;\n        addSlot(new SlotTemplateManager(itemHandler, 0, x, 18, TEXTURE_LOC_SLOT_TOOL));\n        addSlot(new SlotTemplateManager(itemHandler, 1, x, 63, TEXTURE_LOC_SLOT_TEMPLATE));\n    }\n\n    @Override\n    @Nonnull\n    public ItemStack quickMoveStack(Player player, int index) {\n        ItemStack itemstack = ItemStack.EMPTY;\n        Slot slot = this.slots.get(index);\n\n        if (slot != null && slot.hasItem()) {\n            ItemStack currentStack = slot.getItem();\n            itemstack = currentStack.copy();\n\n            if (index < TemplateManagerTileEntity.SIZE) {\n                if (! this.moveItemStackTo(currentStack, TemplateManagerTileEntity.SIZE, this.slots.size(), true)) {\n                    return ItemStack.EMPTY;\n                }\n            } else if (! this.moveItemStackTo(currentStack, 0, TemplateManagerTileEntity.SIZE, false)) {\n                return ItemStack.EMPTY;\n            }\n\n            if (currentStack.isEmpty()) {\n                slot.set(ItemStack.EMPTY);\n            } else {\n                slot.setChanged();\n            }\n        }\n\n        return itemstack;\n    }\n\n    public TemplateManagerTileEntity getTe() {\n        return te;\n    }\n\n    public static class SlotTemplateManager extends SlotItemHandler {\n        private String backgroundLoc;\n\n        public SlotTemplateManager(IItemHandler itemHandler, int index, int xPosition, int yPosition, String backgroundLoc) {\n            super(itemHandler, index, xPosition, yPosition);\n            this.backgroundLoc = backgroundLoc;\n            this.setBackground(InventoryMenu.BLOCK_ATLAS, new ResourceLocation(this.backgroundLoc));\n        }\n\n        @Override\n        public int getMaxStackSize() {\n            return 1;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/entities/ConstructionBlockEntity.java",
    "content": "package com.direwolf20.buildinggadgets.common.entities;\n\nimport com.direwolf20.buildinggadgets.common.blocks.ConstructionBlock;\nimport com.direwolf20.buildinggadgets.common.blocks.ConstructionBlockPowder;\nimport com.direwolf20.buildinggadgets.common.blocks.OurBlocks;\nimport com.direwolf20.buildinggadgets.common.tainted.building.BlockData;\nimport com.direwolf20.buildinggadgets.common.tileentities.ConstructionBlockTileEntity;\nimport com.direwolf20.buildinggadgets.common.util.ref.NBTKeys;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.network.protocol.Packet;\nimport net.minecraft.network.protocol.game.ClientGamePacketListener;\nimport net.minecraft.network.syncher.EntityDataAccessor;\nimport net.minecraft.network.syncher.EntityDataSerializers;\nimport net.minecraft.network.syncher.SynchedEntityData;\nimport net.minecraft.world.entity.EntityType;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.Block;\nimport net.minecraft.world.level.block.entity.BlockEntity;\nimport net.minecraftforge.network.NetworkHooks;\n\npublic class ConstructionBlockEntity extends EntityBase {\n    private static final EntityDataAccessor<BlockPos> FIXED = SynchedEntityData.defineId(ConstructionBlockEntity.class, EntityDataSerializers.BLOCK_POS);\n    private static final EntityDataAccessor<Boolean> MAKING = SynchedEntityData.defineId(ConstructionBlockEntity.class, EntityDataSerializers.BOOLEAN);\n\n\n    public ConstructionBlockEntity(Level world, BlockPos spawnPos, boolean makePaste) {\n        this(OurEntities.CONSTRUCTION_BLOCK_ENTITY.get(), world);\n\n        setPos(spawnPos.getX(), spawnPos.getY(), spawnPos.getZ());\n        targetPos = spawnPos;\n        setMakingPaste(makePaste);\n    }\n\n    public ConstructionBlockEntity(EntityType<?> constructionBlockEntityEntityType, Level level) {\n        super(constructionBlockEntityEntityType, level);\n    }\n\n    public ConstructionBlockEntity(Level level) {\n        super(OurEntities.CONSTRUCTION_BLOCK_ENTITY.get(), level);\n    }\n\n    @Override\n    protected int getMaxLife() {\n        return 80;\n    }\n\n    @Override\n    protected void defineSynchedData() {\n        entityData.define(FIXED, BlockPos.ZERO);\n        entityData.define(MAKING, false);\n    }\n\n    @Override\n    protected boolean shouldSetDespawning() {\n        if (super.shouldSetDespawning())\n            return true;\n\n        if (targetPos == null)\n            return false;\n\n        Block block = level.getBlockState(targetPos).getBlock();\n        return !(block instanceof ConstructionBlock) && !(block instanceof ConstructionBlockPowder);\n    }\n\n    @Override\n    protected void onSetDespawning() {\n        if (targetPos != null) {\n            if (!getMakingPaste()) {\n                BlockEntity te = level.getBlockEntity(targetPos);\n                if (te instanceof ConstructionBlockTileEntity) {\n                    BlockData tempState = ((ConstructionBlockTileEntity) te).getConstructionBlockData();\n\n                    boolean opaque = tempState.getState().isSolidRender(level, targetPos);\n                    boolean neighborBrightness = false;//tempState.useNeighbourBrightness(world, targetPos); //TODO find replacement\n                    //IBakedModel model;\n                    //model = Minecraft.getInstance().getBlockRendererDispatcher().getBlockModelShapes().getModel(tempState.getState());\n                    //boolean ambient = model.isAmbientOcclusion();\n                    boolean ambient = false; //TODO Find a better way to get the proper ambient Occlusion value. This is client side only so can't be done here.\n                    if (opaque || neighborBrightness || !ambient) {\n                        BlockData tempSetBlock = ((ConstructionBlockTileEntity) te).getConstructionBlockData();\n                        level.setBlockAndUpdate(targetPos, OurBlocks.CONSTRUCTION_BLOCK.get().defaultBlockState()\n                                .setValue(ConstructionBlock.BRIGHT, !opaque)\n                                .setValue(ConstructionBlock.NEIGHBOR_BRIGHTNESS, neighborBrightness)\n                                .setValue(ConstructionBlock.AMBIENT_OCCLUSION, ambient));\n                        te = level.getBlockEntity(targetPos);\n                        if (te instanceof ConstructionBlockTileEntity) {\n                            ((ConstructionBlockTileEntity) te).setBlockState(tempSetBlock);\n                        }\n                    }\n                }\n            } else if (level.getBlockState(targetPos) == OurBlocks.CONSTRUCTION_POWDER_BLOCK.get().defaultBlockState()) {\n                level.setBlockAndUpdate(targetPos, OurBlocks.CONSTRUCTION_DENSE_BLOCK.get().defaultBlockState());\n            }\n        }\n    }\n\n    public void setMakingPaste(boolean paste) {\n        entityData.set(MAKING, paste);\n    }\n\n    public boolean getMakingPaste() {\n        return entityData.get(MAKING);\n    }\n\n    @Override\n    protected void readAdditionalSaveData(CompoundTag compound) {\n        super.readAdditionalSaveData(compound);\n        setMakingPaste(compound.getBoolean(NBTKeys.ENTITY_CONSTRUCTION_MAKING_PASTE));\n    }\n\n    @Override\n    protected void addAdditionalSaveData(CompoundTag compound) {\n        super.addAdditionalSaveData(compound);\n        compound.putBoolean(NBTKeys.ENTITY_CONSTRUCTION_MAKING_PASTE, getMakingPaste());\n    }\n\n    @Override\n    public Packet<ClientGamePacketListener> getAddEntityPacket() {\n        return NetworkHooks.getEntitySpawningPacket(this);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/entities/ConstructionBlockEntityRender.java",
    "content": "package com.direwolf20.buildinggadgets.common.entities;\n\nimport com.direwolf20.buildinggadgets.client.renderer.OurRenderTypes;\nimport com.direwolf20.buildinggadgets.common.blocks.OurBlocks;\nimport com.mojang.blaze3d.vertex.PoseStack;\nimport com.mojang.blaze3d.vertex.VertexConsumer;\nimport net.minecraft.client.Minecraft;\nimport net.minecraft.client.color.block.BlockColors;\nimport net.minecraft.client.renderer.MultiBufferSource;\nimport net.minecraft.client.renderer.block.BlockRenderDispatcher;\nimport net.minecraft.client.renderer.entity.EntityRenderer;\nimport net.minecraft.client.renderer.entity.EntityRendererProvider;\nimport net.minecraft.client.resources.model.BakedModel;\nimport net.minecraft.core.Direction;\nimport net.minecraft.resources.ResourceLocation;\nimport net.minecraft.util.Mth;\nimport net.minecraft.util.RandomSource;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraftforge.client.model.data.ModelData;\n\nimport static com.direwolf20.buildinggadgets.client.renderer.MyRenderMethods.renderModelBrightnessColorQuads;\n\npublic class ConstructionBlockEntityRender extends EntityRenderer<ConstructionBlockEntity> {\n\n    public ConstructionBlockEntityRender(EntityRendererProvider.Context renderManager) {\n        super(renderManager);\n        this.shadowRadius = 0F;\n    }\n\n    @Override\n    public void render(ConstructionBlockEntity entityIn, float entityYaw, float partialTicks, PoseStack matrixStackIn, MultiBufferSource bufferIn, int packedLightIn) {\n        BlockRenderDispatcher blockrendererdispatcher = Minecraft.getInstance().getBlockRenderer();\n        MultiBufferSource.BufferSource buffer = Minecraft.getInstance().renderBuffers().bufferSource();\n        VertexConsumer builder;\n        Minecraft mc = Minecraft.getInstance();\n        builder = buffer.getBuffer(OurRenderTypes.RenderBlock);\n        BlockState renderBlockState = OurBlocks.CONSTRUCTION_DENSE_BLOCK.get().defaultBlockState();\n        int teCounter = entityIn.tickCount;\n        int maxLife = entityIn.getMaxLife();\n        teCounter = Math.min(teCounter, maxLife);\n        float scale = (float) (maxLife - teCounter) / maxLife;\n        if (entityIn.getMakingPaste())\n            scale = (float) teCounter / maxLife;\n        matrixStackIn.pushPose();\n        matrixStackIn.translate(-0.0005f, -0.0005f, -0.0005f);\n        matrixStackIn.scale(1.001f, 1.001f, 1.001f);//Slightly Larger block to avoid z-fighting.\n        BlockColors blockColors = Minecraft.getInstance().getBlockColors();\n        int color = blockColors.getColor(renderBlockState, mc.player.level, entityIn.blockPosition(), 0);\n        float f = (float) (color >> 16 & 255) / 255.0F;\n        float f1 = (float) (color >> 8 & 255) / 255.0F;\n        float f2 = (float) (color & 255) / 255.0F;\n        BakedModel ibakedmodel = blockrendererdispatcher.getBlockModel(renderBlockState);\n        for (Direction direction : Direction.values()) {\n            renderModelBrightnessColorQuads(matrixStackIn.last(), builder, f, f1, f2, scale, ibakedmodel.getQuads(renderBlockState, direction, RandomSource.create(Mth.getSeed(entityIn.blockPosition())), ModelData.EMPTY, OurRenderTypes.RenderBlock), 15728640, 655360);\n        }\n        matrixStackIn.popPose();\n    }\n\n    @Override\n    public ResourceLocation getTextureLocation(ConstructionBlockEntity entity) {\n        return null;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/entities/EntityBase.java",
    "content": "package com.direwolf20.buildinggadgets.common.entities;\n\nimport com.direwolf20.buildinggadgets.common.util.ref.NBTKeys;\nimport net.minecraft.world.entity.Entity;\nimport net.minecraft.world.entity.EntityType;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.nbt.NbtUtils;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.world.level.Level;\n\npublic abstract class EntityBase extends Entity {\n    private int despawning = -1;\n    protected BlockPos targetPos;\n\n    public EntityBase(EntityType<?> entityType, Level world) {\n        super(entityType, world);\n    }\n\n    protected abstract int getMaxLife();\n\n    protected abstract void onSetDespawning();\n\n    @Override\n    public void baseTick() {\n        super.baseTick();\n        if (despawning == -1 && shouldSetDespawning()) {\n            despawning = 0;\n            onSetDespawning();\n        } else if (despawning != -1 && ++despawning > 1)\n            remove(RemovalReason.DISCARDED);\n    }\n\n    protected boolean shouldSetDespawning() {\n        return tickCount > getMaxLife();\n    }\n\n    @Override\n    protected void readAdditionalSaveData(CompoundTag compound) {\n        despawning = compound.getInt(NBTKeys.ENTITY_DESPAWNING);\n        tickCount = compound.getInt(NBTKeys.ENTITY_TICKS_EXISTED);\n        targetPos = NbtUtils.readBlockPos(compound.getCompound(NBTKeys.ENTITY_SET_POS));\n    }\n\n    @Override\n    protected void addAdditionalSaveData(CompoundTag compound) {\n        compound.putInt(NBTKeys.ENTITY_DESPAWNING, despawning);\n        compound.putInt(NBTKeys.ENTITY_TICKS_EXISTED, tickCount);\n        compound.put(NBTKeys.ENTITY_SET_POS, NbtUtils.writeBlockPos(targetPos));\n    }\n\n    @Override\n    public boolean shouldRender(double x, double y, double z) {\n        return true;\n    }\n}"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/entities/OurEntities.java",
    "content": "package com.direwolf20.buildinggadgets.common.entities;\n\nimport com.direwolf20.buildinggadgets.common.util.ref.Reference;\nimport net.minecraft.world.entity.EntityType;\nimport net.minecraft.world.entity.MobCategory;\nimport net.minecraftforge.api.distmarker.Dist;\nimport net.minecraftforge.client.event.EntityRenderersEvent;\nimport net.minecraftforge.eventbus.api.SubscribeEvent;\nimport net.minecraftforge.fml.common.Mod;\nimport net.minecraftforge.registries.DeferredRegister;\nimport net.minecraftforge.registries.ForgeRegistries;\nimport net.minecraftforge.registries.RegistryObject;\n\n@Mod.EventBusSubscriber(modid = Reference.MODID, value = Dist.CLIENT, bus = Mod.EventBusSubscriber.Bus.MOD)\npublic class OurEntities {\n    public static final DeferredRegister<EntityType<?>> ENTITY_REGISTER = DeferredRegister.create(ForgeRegistries.ENTITY_TYPES, Reference.MODID);\n\n    public static final RegistryObject<EntityType<ConstructionBlockEntity>> CONSTRUCTION_BLOCK_ENTITY = ENTITY_REGISTER.register(\"construction_block_entity\", () -> EntityType.Builder.<ConstructionBlockEntity>of(ConstructionBlockEntity::new, MobCategory.MISC)\n            .setTrackingRange(64)\n            .setUpdateInterval(1)\n            .setShouldReceiveVelocityUpdates(false)\n            .setCustomClientFactory(((spawnEntity, world) -> new ConstructionBlockEntity(world)))\n            .build(\"\"));\n\n    @SubscribeEvent\n    public static void registerModels(EntityRenderersEvent.RegisterRenderers event) {\n        event.registerEntityRenderer(CONSTRUCTION_BLOCK_ENTITY.get(), ConstructionBlockEntityRender::new);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/events/BreakEventHandler.java",
    "content": "package com.direwolf20.buildinggadgets.common.events;\n\nimport net.minecraftforge.event.level.BlockEvent;\nimport net.minecraftforge.eventbus.api.SubscribeEvent;\nimport net.minecraftforge.fml.common.Mod.EventBusSubscriber;\n\n@EventBusSubscriber\npublic class BreakEventHandler {\n    @SubscribeEvent\n    public static void GetDrops(BlockEvent.BreakEvent event) {\n        //If you are holding an exchanger gadget and break a block, put it into your inventory\n        //This allows us to use the BreakBlock event on our exchanger, to properly remove blocks from the world.\n//        PlayerEntity player = event.getPlayer();\n//        if (player == null)\n//            return;\n//\n//        ItemStack heldItem = AbstractGadget.getGadget(player);\n//        if (heldItem.isEmpty())\n//            return;\n//\n//        List<ItemStack> drops = Block.getDrops(event.getState(), (ServerWorld) event.getWorld(), event.getPos(), event.getWorld().getTileEntity(event.getPos()));\n//        drops.removeIf(item -> InventoryHelper.giveItem(item, player, event.getPlayer().world));\n    }\n}\n\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/events/ItemPickupHandler.java",
    "content": "package com.direwolf20.buildinggadgets.common.events;\n\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.InventoryHelper;\nimport com.direwolf20.buildinggadgets.common.items.ConstructionPaste;\nimport net.minecraft.world.entity.item.ItemEntity;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraftforge.event.entity.player.EntityItemPickupEvent;\nimport net.minecraftforge.eventbus.api.SubscribeEvent;\nimport net.minecraftforge.fml.common.Mod.EventBusSubscriber;\n\n@EventBusSubscriber\npublic class ItemPickupHandler {\n\n    @SubscribeEvent\n    public static void GetDrops(EntityItemPickupEvent event) {\n        ItemEntity entityItem = event.getItem();\n        ItemStack itemStack = entityItem.getItem();\n\n        if (itemStack.getItem() instanceof ConstructionPaste) {\n            itemStack = InventoryHelper.addPasteToContainer(event.getEntity(), itemStack);\n            entityItem.setItem(itemStack);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/events/WorldTemplateProviderHandler.java",
    "content": "package com.direwolf20.buildinggadgets.common.events;\n\nimport com.direwolf20.buildinggadgets.client.ClientProxy;\nimport com.direwolf20.buildinggadgets.common.capability.provider.TemplateProviderCapabilityProvider;\nimport com.direwolf20.buildinggadgets.common.tainted.save.SaveManager;\nimport com.direwolf20.buildinggadgets.common.tainted.template.ITemplateProvider;\nimport com.direwolf20.buildinggadgets.common.util.ref.Reference;\nimport net.minecraft.world.level.Level;\nimport net.minecraftforge.event.AttachCapabilitiesEvent;\nimport net.minecraftforge.eventbus.api.SubscribeEvent;\nimport net.minecraftforge.fml.common.Mod.EventBusSubscriber;\n\n/**\n * Event handler for attaching the TemplateProvider capability to worlds.\n */\n@EventBusSubscriber\npublic final class WorldTemplateProviderHandler {\n\n    @SubscribeEvent\n    public static void onAttachWorldCapabilities(AttachCapabilitiesEvent<Level> event) {\n        if (event.getObject().isClientSide())\n            insertProvider(event, ClientProxy.CACHE_TEMPLATE_PROVIDER);\n        else\n            insertProvider(event, SaveManager.INSTANCE.getTemplateProvider());\n    }\n\n    private static void insertProvider(AttachCapabilitiesEvent<Level> event, ITemplateProvider provider) {\n        event.addCapability(Reference.WORLD_TEMPLATE_PROVIDER_ID, new TemplateProviderCapabilityProvider(provider));\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/integration/IntegrationHandler.java",
    "content": "//package com.direwolf20.buildinggadgets.common.integration;\n//\n//import java.lang.annotation.Retention;\n//import java.lang.annotation.RetentionPolicy;\n//\n////import com.direwolf20.buildinggadgets.common.BuildingGadgets;\n////\n////import net.minecraftforge.fml.common.Loader;\n////import net.minecraftforge.fml.common.discovery.ASMDataTable.ASMData;\n////import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;\n//\n//public class IntegrationHandler {// TODO 1.13\n//\n////    public static void preInit(FMLPreInitializationEvent event) {\n////        for (ASMData asmData : event.getAsmData().getAll(IntegratedMod.class.getName())) {\n////            String name = asmData.getClassName();\n////            try {\n////                if (Loader.isModLoaded((String) asmData.getAnnotationInfo().get(\"value\")))\n////                    Class.forName(name).asSubclass(IIntegratedMod.class).newInstance().initialize();\n////            } catch (Exception e) {\n////                BuildingGadgets.logger.error(String.format(\"Integration with %s failed\", name), e);\n////            }\n////        }\n////    }\n//\n//    @Retention(RetentionPolicy.RUNTIME)\n//    public static @interface IntegratedMod {\n//        String value();\n//    }\n//\n//    public static interface IIntegratedMod {\n//\n//        void initialize();\n//    }\n//}"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/integration/RefinedStorage.java",
    "content": "//package com.direwolf20.buildinggadgets.common.integration;\n//\n//import com.direwolf20.buildinggadgets.common.integration.IntegrationHandler.IIntegratedMod;\n//import com.direwolf20.buildinggadgets.common.integration.IntegrationHandler.IntegratedMod;\n//import com.direwolf20.buildinggadgets.common.util.tools.NetworkIO;\n//import net.minecraft.tileentity.TileEntity;\n//import net.minecraftforge.items.IItemHandler;\n//\n//import javax.annotation.Nullable;\n//\n///*\n//import com.direwolf20.buildinggadgets.common.util.tools.NetworkIO.NetworkRefinedStorageIO;\n//import com.raoulvdberge.refinedstorage.api.network.INetwork;\n//import com.raoulvdberge.refinedstorage.api.network.node.INetworkNodeProxy;\n//*/\n//\n//@IntegratedMod(\"refinedstorage\")\n//public class RefinedStorage implements IIntegratedMod {\n//    private static boolean isLoaded;\n//\n//    @Override\n//    public void initialize() {\n//        isLoaded = true;\n//    }\n//\n//    @Nullable\n//    public static IItemHandler getWrappedNetwork(TileEntity te, NetworkIO.Operation operation) {\n//        /*if (isLoaded && te instanceof INetworkNodeProxy) {\n//            INetwork network = ((INetworkNodeProxy) te).getNode().getNetwork();\n//            if (network != null)\n//                return new NetworkRefinedStorageIO(network, operation);\n//        }*/\n//        return null;\n//    }\n//}"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/items/AbstractGadget.java",
    "content": "package com.direwolf20.buildinggadgets.common.items;\n\n\nimport com.direwolf20.buildinggadgets.client.renders.BaseRenderer;\nimport com.direwolf20.buildinggadgets.common.BuildingGadgets;\nimport com.direwolf20.buildinggadgets.common.capability.CapabilityProviderEnergy;\nimport com.direwolf20.buildinggadgets.common.capability.IPrivateEnergy;\nimport com.direwolf20.buildinggadgets.common.capability.provider.MultiCapabilityProvider;\nimport com.direwolf20.buildinggadgets.common.commands.ForceUnloadedCommand;\nimport com.direwolf20.buildinggadgets.common.config.Config;\nimport com.direwolf20.buildinggadgets.common.items.modes.*;\nimport com.direwolf20.buildinggadgets.common.tainted.building.view.BuildContext;\nimport com.direwolf20.buildinggadgets.common.tainted.concurrent.UndoScheduler;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.IItemIndex;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.InventoryHelper;\nimport com.direwolf20.buildinggadgets.common.tainted.save.SaveManager;\nimport com.direwolf20.buildinggadgets.common.tainted.save.Undo;\nimport com.direwolf20.buildinggadgets.common.tainted.save.UndoWorldSave;\nimport com.direwolf20.buildinggadgets.common.util.GadgetUtils;\nimport com.direwolf20.buildinggadgets.common.util.helpers.VectorHelper;\nimport com.direwolf20.buildinggadgets.common.util.lang.MessageTranslation;\nimport com.direwolf20.buildinggadgets.common.util.lang.Styles;\nimport com.direwolf20.buildinggadgets.common.util.lang.TooltipTranslation;\nimport com.direwolf20.buildinggadgets.common.util.ref.NBTKeys;\nimport com.google.common.collect.ImmutableList;\nimport com.google.common.collect.ImmutableSortedSet;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.registries.Registries;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.resources.ResourceLocation;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.minecraft.tags.TagKey;\nimport net.minecraft.util.Mth;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.item.Item;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.Items;\nimport net.minecraft.world.level.ChunkPos;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.Block;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.phys.BlockHitResult;\nimport net.minecraftforge.api.distmarker.Dist;\nimport net.minecraftforge.api.distmarker.OnlyIn;\nimport net.minecraftforge.common.capabilities.ForgeCapabilities;\nimport net.minecraftforge.common.capabilities.ICapabilityProvider;\nimport net.minecraftforge.common.util.LazyOptional;\nimport net.minecraftforge.energy.IEnergyStorage;\nimport net.minecraftforge.fml.DistExecutor;\nimport net.minecraftforge.registries.ForgeRegistries;\nimport net.minecraftforge.registries.tags.ITagManager;\nimport org.apache.commons.lang3.tuple.Pair;\n\nimport javax.annotation.Nullable;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.UUID;\nimport java.util.function.IntSupplier;\nimport java.util.function.Supplier;\n\nimport static com.direwolf20.buildinggadgets.common.util.GadgetUtils.withSuffix;\n\npublic abstract class AbstractGadget extends Item {\n    private BaseRenderer renderer;\n    private final TagKey<Block> whiteList;\n    private final TagKey<Block> blackList;\n    private Supplier<UndoWorldSave> saveSupplier;\n\n    public AbstractGadget(Properties builder, IntSupplier undoLengthSupplier, String undoName, ResourceLocation whiteListTag, ResourceLocation blackListTag) {\n        super(builder.setNoRepair());\n\n        renderer = DistExecutor.runForDist(this::createRenderFactory, () -> () -> null);\n        this.whiteList = TagKey.create(Registries.BLOCK, whiteListTag);\n        this.blackList = TagKey.create(Registries.BLOCK, blackListTag);\n        saveSupplier = SaveManager.INSTANCE.registerUndoSave(w -> SaveManager.getUndoSave(w, undoLengthSupplier, undoName));\n    }\n\n    public abstract int getEnergyMax();\n\n    public abstract int getEnergyCost(ItemStack tool);\n\n    public TagKey<Block> getWhiteList() {\n        return whiteList;\n    }\n\n    public TagKey<Block> getBlackList() {\n        return blackList;\n    }\n\n    @OnlyIn(Dist.CLIENT)\n    public BaseRenderer getRender() {\n        return renderer;\n    }\n\n    protected abstract Supplier<BaseRenderer> createRenderFactory();\n\n    protected UndoWorldSave getUndoSave() {\n        return saveSupplier.get();\n    }\n\n    protected void addCapabilityProviders(ImmutableList.Builder<ICapabilityProvider> providerBuilder, ItemStack stack, @Nullable CompoundTag tag) {\n        providerBuilder.add(new CapabilityProviderEnergy(stack, this::getEnergyMax));\n    }\n\n    @Override\n    @Nullable\n    public ICapabilityProvider initCapabilities(ItemStack stack, @Nullable CompoundTag tag) {\n        ImmutableList.Builder<ICapabilityProvider> providerBuilder = ImmutableList.builder();\n        addCapabilityProviders(providerBuilder, stack, tag);\n        return new MultiCapabilityProvider(providerBuilder.build());\n    }\n\n    @Override\n    public int getBarWidth(ItemStack stack) {\n        LazyOptional<IEnergyStorage> cap = stack.getCapability(ForgeCapabilities.ENERGY);\n        if (!cap.isPresent())\n            return super.getBarWidth(stack);\n\n        return cap.map(e -> Math.min(13 * e.getEnergyStored() / e.getMaxEnergyStored(), 13))\n                .orElse(super.getBarWidth(stack));\n    }\n\n    @Override\n    public int getBarColor(ItemStack stack) {\n        LazyOptional<IEnergyStorage> cap = stack.getCapability(ForgeCapabilities.ENERGY);\n        if (!cap.isPresent())\n            return super.getBarColor(stack);\n\n        Pair<Integer, Integer> energyStorage = cap.map(e -> Pair.of(e.getEnergyStored(), e.getMaxEnergyStored())).orElse(Pair.of(0, 0));\n        return Mth.hsvToRgb(Math.max(0.0F, energyStorage.getLeft() / (float) energyStorage.getRight()) / 3.0F, 1.0F, 1.0F);\n    }\n\n    @Override\n    public boolean isDamaged(ItemStack stack) {\n        LazyOptional<IEnergyStorage> cap = stack.getCapability(ForgeCapabilities.ENERGY);\n        if (!cap.isPresent())\n            return super.isDamaged(stack);\n\n        Pair<Integer, Integer> energyStorage = cap.map(e -> Pair.of(e.getEnergyStored(), e.getMaxEnergyStored())).orElse(Pair.of(0, 0));\n        return energyStorage.getLeft() != energyStorage.getRight();\n    }\n\n\n    @Override\n    public boolean isBarVisible(ItemStack stack) {\n        if (stack.hasTag() && stack.getTag().contains(NBTKeys.CREATIVE_MARKER))\n            return false;\n\n        return stack.getCapability(ForgeCapabilities.ENERGY).map(e -> e.getEnergyStored() != e.getMaxEnergyStored()).orElse(super.isBarVisible(stack));\n    }\n\n    @Override\n    public boolean isValidRepairItem(ItemStack toRepair, ItemStack repair) {\n        return !toRepair.getCapability(ForgeCapabilities.ENERGY).isPresent() && repair.getItem() == Items.DIAMOND;\n    }\n\n    public boolean isAllowedBlock(BlockState block) {\n        ITagManager<Block> tags = ForgeRegistries.BLOCKS.tags();\n        if (tags != null && tags.getTag(getWhiteList()).isEmpty()) {\n            return !block.is(getBlackList());\n        }\n\n        return block.is(getWhiteList());\n    }\n\n    public static ItemStack getGadget(Player player) {\n        ItemStack heldItem = player.getMainHandItem();\n        if (!(heldItem.getItem() instanceof AbstractGadget)) {\n            heldItem = player.getOffhandItem();\n            if (!(heldItem.getItem() instanceof AbstractGadget)) {\n                return ItemStack.EMPTY;\n            }\n        }\n        return heldItem;\n    }\n\n    public boolean canUse(ItemStack tool, Player player) {\n        if (player.isCreative() || getEnergyMax() == 0)\n            return true;\n\n        return getEnergyCost(tool) <= tool.getCapability(ForgeCapabilities.ENERGY).map(IEnergyStorage::getEnergyStored).orElse(0);\n    }\n\n    public void applyDamage(ItemStack tool, ServerPlayer player) {\n        if (player.isCreative() || getEnergyMax() == 0)\n            return;\n\n        tool.getCapability(ForgeCapabilities.ENERGY).ifPresent(e -> ((IPrivateEnergy) e).extractPower(getEnergyCost(tool), false));\n    }\n\n    protected void addEnergyInformation(List<Component> tooltip, ItemStack stack) {\n        if (getEnergyMax() == 0)\n            return;\n\n        stack.getCapability(ForgeCapabilities.ENERGY).ifPresent(energy -> {\n            tooltip.add(TooltipTranslation.GADGET_ENERGY\n                    .componentTranslation(withSuffix(energy.getEnergyStored()), withSuffix(energy.getMaxEnergyStored()))\n                    .setStyle(Styles.GRAY));\n        });\n    }\n\n    public final void onRotate(ItemStack stack, Player player) {\n        if (performRotate(stack, player))\n            player.displayClientMessage(MessageTranslation.ROTATED.componentTranslation().setStyle(Styles.AQUA), true);\n    }\n\n    protected boolean performRotate(ItemStack stack, Player player) {\n        return false;\n    }\n\n    public final void onMirror(ItemStack stack, Player player) {\n        if (performMirror(stack, player))\n            player.displayClientMessage(MessageTranslation.MIRRORED.componentTranslation().setStyle(Styles.AQUA), true);\n    }\n\n    protected boolean performMirror(ItemStack stack, Player player) {\n        return false;\n    }\n\n    public final void onAnchor(ItemStack stack, Player player) {\n        if (getAnchor(stack) == null) {\n            BlockHitResult lookingAt = VectorHelper.getLookingAt(player, stack);\n            if ((player.level.isEmptyBlock(lookingAt.getBlockPos())))\n                return;\n            onAnchorSet(stack, player, lookingAt);\n            player.displayClientMessage(MessageTranslation.ANCHOR_SET.componentTranslation().setStyle(Styles.AQUA), true);\n        } else {\n            onAnchorRemoved(stack, player);\n            player.displayClientMessage(MessageTranslation.ANCHOR_REMOVED.componentTranslation().setStyle(Styles.AQUA), true);\n        }\n    }\n\n    protected void onAnchorSet(ItemStack stack, Player player, BlockHitResult lookingAt) {\n        GadgetUtils.writePOSToNBT(stack, lookingAt.getBlockPos(), NBTKeys.GADGET_ANCHOR);\n    }\n\n    protected void onAnchorRemoved(ItemStack stack, Player player) {\n        stack.getOrCreateTag().remove(NBTKeys.GADGET_ANCHOR);\n    }\n\n    @Nullable\n    public BlockPos getAnchor(ItemStack stack) {\n        return GadgetUtils.getPOSFromNBT(stack, NBTKeys.GADGET_ANCHOR);\n    }\n\n    public static boolean getFuzzy(ItemStack stack) {\n        return stack.getOrCreateTag().getBoolean(NBTKeys.GADGET_FUZZY);\n    }\n\n    public static void toggleFuzzy(Player player, ItemStack stack) {\n        stack.getOrCreateTag().putBoolean(NBTKeys.GADGET_FUZZY, !getFuzzy(stack));\n        player.displayClientMessage(MessageTranslation.FUZZY_MODE.componentTranslation(getFuzzy(stack)).setStyle(Styles.AQUA), true);\n    }\n\n    public static boolean getConnectedArea(ItemStack stack) {\n        return !stack.getOrCreateTag().getBoolean(NBTKeys.GADGET_UNCONNECTED_AREA);\n    }\n\n    public static void toggleConnectedArea(Player player, ItemStack stack) {\n        stack.getOrCreateTag().putBoolean(NBTKeys.GADGET_UNCONNECTED_AREA, getConnectedArea(stack));\n        player.displayClientMessage((stack.getItem() instanceof GadgetDestruction ? MessageTranslation.CONNECTED_AREA : MessageTranslation.CONNECTED_SURFACE)\n                .componentTranslation(getConnectedArea(stack)).setStyle(Styles.AQUA), true);\n    }\n\n    public static boolean shouldRayTraceFluid(ItemStack stack) {\n        return stack.getOrCreateTag().getBoolean(NBTKeys.GADGET_RAYTRACE_FLUID);\n    }\n\n    public static void toggleRayTraceFluid(ServerPlayer player, ItemStack stack) {\n        stack.getOrCreateTag().putBoolean(NBTKeys.GADGET_RAYTRACE_FLUID, !shouldRayTraceFluid(stack));\n        player.displayClientMessage(MessageTranslation.RAYTRACE_FLUID.componentTranslation(shouldRayTraceFluid(stack)).setStyle(Styles.AQUA), true);\n    }\n\n    public static void addInformationRayTraceFluid(List<Component> tooltip, ItemStack stack) {\n        tooltip.add(TooltipTranslation.GADGET_RAYTRACE_FLUID\n                .componentTranslation(String.valueOf(shouldRayTraceFluid(stack)))\n                .setStyle(Styles.BLUE));\n    }\n\n    //this should only be called Server-Side!!!\n    public UUID getUUID(ItemStack stack) {\n        CompoundTag nbt = stack.getOrCreateTag();\n        if (!nbt.hasUUID(NBTKeys.GADGET_UUID)) {\n            UUID newId = getUndoSave().getFreeUUID();\n            nbt.putUUID(NBTKeys.GADGET_UUID, newId);\n            return newId;\n        }\n        return nbt.getUUID(NBTKeys.GADGET_UUID);\n    }\n\n    // Todo: tweak and fix.\n    public static int getRangeInBlocks(int range, AbstractMode mode) {\n        if (mode instanceof StairMode ||\n                mode instanceof VerticalColumnMode ||\n                mode instanceof HorizontalColumnMode)\n            return range;\n\n        if (mode instanceof GridMode)\n            return range < 7 ? 9 : range < 13 ? 11 * 11 : 19 * 19;\n\n        return range == 1 ? 1 : (range + 1) * (range + 1);\n    }\n\n    protected void pushUndo(ItemStack stack, Undo undo) {\n        // Don't save if there is nothing to undo...\n        if (undo.getUndoData().isEmpty()) {\n            return;\n        }\n\n        UndoWorldSave save = getUndoSave();\n        save.insertUndo(getUUID(stack), undo);\n    }\n\n    public void undo(Level world, Player player, ItemStack stack) {\n        UndoWorldSave save = getUndoSave();\n        Optional<Undo> undoOptional = save.getUndo(getUUID(stack));\n\n        if (undoOptional.isPresent()) {\n            Undo undo = undoOptional.get();\n            IItemIndex index = InventoryHelper.index(stack, player);\n            if (!ForceUnloadedCommand.mayForceUnloadedChunks(player)) {//TODO separate command\n                ImmutableSortedSet<ChunkPos> unloadedChunks = undo.getBoundingBox().getUnloadedChunks(world);\n                if (!unloadedChunks.isEmpty()) {\n                    pushUndo(stack, undo);\n                    player.displayClientMessage(MessageTranslation.UNDO_UNLOADED.componentTranslation().setStyle(Styles.RED), true);\n                    BuildingGadgets.LOG.error(\"Player attempted to undo a Region missing {} unloaded chunks. Denied undo!\", unloadedChunks.size());\n                    BuildingGadgets.LOG.trace(\"The following chunks were detected as unloaded {}.\", unloadedChunks);\n                    return;\n                }\n            }\n            BuildContext buildContext = BuildContext.builder()\n                    .player(player)\n                    .stack(stack)\n                    .build(world);\n\n            UndoScheduler.scheduleUndo(undo, index, buildContext, Config.GADGETS.placeSteps.get());\n        } else\n            player.displayClientMessage(MessageTranslation.NOTHING_TO_UNDO.componentTranslation().setStyle(Styles.RED), true);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/items/ConstructionPaste.java",
    "content": "package com.direwolf20.buildinggadgets.common.items;\n\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.InventoryHelper;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.item.Item;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.InteractionResultHolder;\nimport net.minecraft.world.InteractionResult;\nimport net.minecraft.world.InteractionHand;\nimport net.minecraft.world.level.Level;\n\npublic class ConstructionPaste extends Item {\n    public ConstructionPaste() {\n        super(OurItems.itemProperties());\n    }\n\n    @Override\n    public InteractionResultHolder<ItemStack> use(Level world, Player player, InteractionHand hand) {\n        ItemStack itemstack = player.getItemInHand(hand);\n        itemstack = InventoryHelper.addPasteToContainer(player, itemstack);\n        return new InteractionResultHolder<>(InteractionResult.SUCCESS, itemstack);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/items/ConstructionPasteContainer.java",
    "content": "package com.direwolf20.buildinggadgets.common.items;\n\nimport com.direwolf20.buildinggadgets.common.BuildingGadgets;\nimport com.direwolf20.buildinggadgets.common.capability.PasteContainerCapabilityProvider;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.InventoryHelper;\nimport com.direwolf20.buildinggadgets.common.util.lang.Styles;\nimport com.direwolf20.buildinggadgets.common.util.lang.TooltipTranslation;\nimport com.direwolf20.buildinggadgets.common.util.ref.NBTKeys;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.network.chat.MutableComponent;\nimport net.minecraft.resources.ResourceLocation;\nimport net.minecraft.world.InteractionHand;\nimport net.minecraft.world.InteractionResult;\nimport net.minecraft.world.InteractionResultHolder;\nimport net.minecraft.world.entity.player.Inventory;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.item.Item;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.TooltipFlag;\nimport net.minecraft.world.level.Level;\nimport net.minecraftforge.common.capabilities.ICapabilityProvider;\n\nimport javax.annotation.Nullable;\nimport java.util.List;\nimport java.util.function.IntSupplier;\n\npublic class ConstructionPasteContainer extends Item {\n    public static final ResourceLocation LEVEL = new ResourceLocation(\"level\");\n\n    private final IntSupplier maxCapacity;\n    private final boolean isCreative;\n\n    public ConstructionPasteContainer(boolean isCreative, IntSupplier maxCapacity) {\n        super(OurItems.nonStackableItemProperties());\n\n        this.isCreative = isCreative;\n        this.maxCapacity = maxCapacity;\n    }\n\n    @Override\n    public Object getRenderPropertiesInternal() {\n        return super.getRenderPropertiesInternal();\n    }\n\n    public ConstructionPasteContainer(boolean isCreative) {\n        this(isCreative, () -> Integer.MAX_VALUE);\n    }\n\n    @Override\n    public ICapabilityProvider initCapabilities(ItemStack stack, @Nullable CompoundTag nbt) {\n        return new PasteContainerCapabilityProvider(stack);\n    }\n\n    public void setPasteCount(ItemStack stack, int amount) {\n        if (isCreative) {\n            return;\n        }\n\n        stack.getOrCreateTag().putInt(NBTKeys.PASTE_COUNT, amount);\n    }\n\n    public int getPasteCount(ItemStack stack) {\n        if (isCreative) {\n            return Integer.MAX_VALUE;\n        }\n\n        return !stack.hasTag() ? 0 : stack.getTag().getInt(NBTKeys.PASTE_COUNT);\n    }\n\n    @Override\n    public InteractionResultHolder<ItemStack> use(Level world, Player player, InteractionHand hand) {\n        ItemStack heldItem = player.getItemInHand(hand);\n        player.startUsingItem(hand);\n        Inventory inv = player.getInventory();\n        if (!world.isClientSide) {\n            for (int i = 0; i < 36; ++i) { // todo: this is awful. hardcoded int\n                ItemStack itemStack = inv.getItem(i);\n                if (itemStack.getItem() instanceof ConstructionPaste) {\n                    InventoryHelper.addPasteToContainer(player, itemStack);\n                }\n            }\n        }\n        return new InteractionResultHolder<>(InteractionResult.SUCCESS, heldItem);\n    }\n\n    @Override\n    public void appendHoverText(ItemStack stack, @Nullable Level world, List<Component> list, TooltipFlag flag) {\n        MutableComponent key = isCreative\n                ? TooltipTranslation.PASTECONTAINER_CREATIVE_AMOUNT.componentTranslation()\n                : TooltipTranslation.PASTECONTAINER_AMOUNT.componentTranslation(getPasteCount(stack), getMaxCapacity());\n\n        list.add(key.setStyle(Styles.WHITE));\n    }\n\n    /**\n     * Helper method. Delegates to {@link ConstructionPasteContainer#setPasteCount(ItemStack, int)}.\n     *\n     * @param stack  current gadget\n     * @param amount amount\n     */\n    public static void setPasteAmount(ItemStack stack, int amount) {\n        Item item = stack.getItem();\n        if (item instanceof ConstructionPasteContainer)\n            ((ConstructionPasteContainer) item).setPasteCount(stack, amount);\n        else\n            BuildingGadgets.LOG.warn(\"Potential abuse of ConstructionPasteContainer#setPasteAmount(ItemStack, int) where the given ItemStack does not contain a ConstructionPasteContainer.\");\n    }\n\n    /**\n     * Helper method. Delegates to {@link ConstructionPasteContainer#getPasteCount(ItemStack)}}.\n     */\n    public static int getPasteAmount(ItemStack stack) {\n        Item item = stack.getItem();\n        if (item instanceof ConstructionPasteContainer)\n            return ((ConstructionPasteContainer) item).getPasteCount(stack);\n\n        return 0;\n    }\n\n    public static int getMaxPasteAmount(ItemStack stack) {\n        Item item = stack.getItem();\n        if (item instanceof ConstructionPasteContainer)\n            return ((ConstructionPasteContainer) item).getMaxCapacity();\n\n        return 0;\n    }\n\n    public int getMaxCapacity() {\n        return isCreative ? Integer.MAX_VALUE : maxCapacity.getAsInt();\n    }\n\n    public boolean isCreative() {\n        return isCreative;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/items/GadgetBuilding.java",
    "content": "package com.direwolf20.buildinggadgets.common.items;\n\nimport com.direwolf20.buildinggadgets.client.renders.BaseRenderer;\nimport com.direwolf20.buildinggadgets.client.renders.BuildRender;\nimport com.direwolf20.buildinggadgets.common.blocks.EffectBlock;\nimport com.direwolf20.buildinggadgets.common.config.Config;\nimport com.direwolf20.buildinggadgets.common.items.modes.AbstractMode;\nimport com.direwolf20.buildinggadgets.common.items.modes.BuildingModes;\nimport com.direwolf20.buildinggadgets.common.network.PacketHandler;\nimport com.direwolf20.buildinggadgets.common.network.packets.PacketBindTool;\nimport com.direwolf20.buildinggadgets.common.network.packets.PacketRotateMirror;\nimport com.direwolf20.buildinggadgets.common.tainted.building.BlockData;\nimport com.direwolf20.buildinggadgets.common.tainted.building.view.BuildContext;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.IItemIndex;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.InventoryHelper;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.MatchResult;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.materials.MaterialList;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.materials.objects.IUniqueObject;\nimport com.direwolf20.buildinggadgets.common.tainted.save.Undo;\nimport com.direwolf20.buildinggadgets.common.util.GadgetUtils;\nimport com.direwolf20.buildinggadgets.common.util.helpers.VectorHelper;\nimport com.direwolf20.buildinggadgets.common.util.lang.LangUtil;\nimport com.direwolf20.buildinggadgets.common.util.lang.MessageTranslation;\nimport com.direwolf20.buildinggadgets.common.util.lang.Styles;\nimport com.direwolf20.buildinggadgets.common.util.lang.TooltipTranslation;\nimport com.direwolf20.buildinggadgets.common.util.ref.NBTKeys;\nimport com.direwolf20.buildinggadgets.common.util.ref.Reference;\nimport com.direwolf20.buildinggadgets.common.util.ref.Reference.BlockReference.TagReference;\nimport com.direwolf20.buildinggadgets.common.util.tools.RegistryUtils;\nimport com.direwolf20.buildinggadgets.common.world.MockBuilderWorld;\nimport com.google.common.collect.ImmutableMultiset;\nimport net.minecraft.client.gui.screens.Screen;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.minecraft.world.InteractionHand;\nimport net.minecraft.world.InteractionResult;\nimport net.minecraft.world.InteractionResultHolder;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.TooltipFlag;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.Block;\nimport net.minecraft.world.level.block.Blocks;\nimport net.minecraft.world.phys.BlockHitResult;\nimport net.minecraftforge.common.util.BlockSnapshot;\nimport net.minecraftforge.event.ForgeEventFactory;\n\nimport javax.annotation.Nullable;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.function.Supplier;\n\nimport static com.direwolf20.buildinggadgets.common.util.GadgetUtils.*;\n\npublic class GadgetBuilding extends AbstractGadget {\n\n    private static final MockBuilderWorld fakeWorld = new MockBuilderWorld();\n\n    public GadgetBuilding() {\n        super(OurItems.nonStackableItemProperties(),\n                Config.GADGETS.GADGET_BUILDING.undoSize::get,\n                Reference.SaveReference.UNDO_BUILDING,\n                TagReference.WHITELIST_BUILDING,\n                TagReference.BLACKLIST_BUILDING);\n    }\n\n    @Override\n    public int getEnergyMax() {\n        return Config.GADGETS.GADGET_BUILDING.maxEnergy.get();\n    }\n\n    @Override\n    public int getEnergyCost(ItemStack tool) {\n        return Config.GADGETS.GADGET_BUILDING.energyCost.get();\n    }\n\n    @Override\n    protected Supplier<BaseRenderer> createRenderFactory() {\n        return () -> new BuildRender(false);\n    }\n\n    public boolean placeAtop(ItemStack stack) {\n        return shouldPlaceAtop(stack);\n    }\n\n    private static void setToolMode(ItemStack tool, BuildingModes mode) {\n        //Store the tool's mode in NBT as a string\n        CompoundTag tagCompound = tool.getOrCreateTag();\n        tagCompound.putString(\"mode\", mode.toString());\n    }\n\n    public static BuildingModes getToolMode(ItemStack tool) {\n        CompoundTag tagCompound = tool.getOrCreateTag();\n        return BuildingModes.getFromName(tagCompound.getString(\"mode\"));\n    }\n\n    public static boolean shouldPlaceAtop(ItemStack stack) {\n        return !stack.getOrCreateTag().getBoolean(NBTKeys.GADGET_PLACE_INSIDE);\n    }\n\n    public static void togglePlaceAtop(Player player, ItemStack stack) {\n        stack.getOrCreateTag().putBoolean(NBTKeys.GADGET_PLACE_INSIDE, shouldPlaceAtop(stack));\n        player.displayClientMessage((shouldPlaceAtop(stack) ? MessageTranslation.PLACE_ATOP : MessageTranslation.PLACE_INSIDE).componentTranslation().setStyle(Styles.AQUA), true);\n    }\n\n    @Override\n    public void appendHoverText(ItemStack stack, @Nullable Level world, List<Component> tooltip, TooltipFlag flag) {\n        super.appendHoverText(stack, world, tooltip, flag);\n        BuildingModes mode = getToolMode(stack);\n        addEnergyInformation(tooltip, stack);\n\n        tooltip.add(TooltipTranslation.GADGET_MODE\n                .componentTranslation((mode == BuildingModes.SURFACE && getConnectedArea(stack)\n                        ? TooltipTranslation.GADGET_CONNECTED.format(Component.translatable(mode.getTranslationKey()).getString())\n                        : Component.translatable(mode.getTranslationKey())))\n                .setStyle(Styles.AQUA));\n\n        tooltip.add(TooltipTranslation.GADGET_BLOCK\n                .componentTranslation(LangUtil.getFormattedBlockName(getToolBlock(stack).getState()))\n                .setStyle(Styles.DK_GREEN));\n\n        int range = getToolRange(stack);\n        if (getToolMode(stack) != BuildingModes.BUILD_TO_ME)\n            tooltip.add(TooltipTranslation.GADGET_RANGE\n                    .componentTranslation(range, getRangeInBlocks(range, mode.getMode()))\n                    .setStyle(Styles.LT_PURPLE));\n\n        if (getToolMode(stack) == BuildingModes.SURFACE)\n            tooltip.add(TooltipTranslation.GADGET_FUZZY\n                    .componentTranslation(String.valueOf(getFuzzy(stack)))\n                    .setStyle(Styles.GOLD));\n\n        addInformationRayTraceFluid(tooltip, stack);\n\n        tooltip.add(TooltipTranslation.GADGET_BUILDING_PLACE_ATOP\n                .componentTranslation(String.valueOf(shouldPlaceAtop(stack)))\n                .setStyle(Styles.YELLOW));\n    }\n\n    @Override\n    public InteractionResultHolder<ItemStack> use(Level world, Player player, InteractionHand hand) {\n        //On item use, if sneaking, select the block clicked on, else build -- This is called when you right click a tool NOT on a block.\n        ItemStack itemstack = player.getItemInHand(hand);\n\n        player.startUsingItem(hand);\n        if (!world.isClientSide) {\n            // Debug code for free energy\n            //itemstack.getCapability(CapabilityEnergy.ENERGY).ifPresent(e -> e.receiveEnergy(15000000, false));\n            if (player.isShiftKeyDown()) {\n                InteractionResultHolder<Block> result = selectBlock(itemstack, player);\n                if (!result.getResult().consumesAction()) {\n                    player.displayClientMessage(MessageTranslation.INVALID_BLOCK.componentTranslation(RegistryUtils.getBlockId(result.getObject())).setStyle(Styles.AQUA), true);\n                    return super.use(world, player, hand);\n                }\n            } else if (player instanceof ServerPlayer) {\n                build((ServerPlayer) player, itemstack);\n            }\n        } else {\n            if (!player.isShiftKeyDown()) {\n                BaseRenderer.updateInventoryCache();\n            } else {\n                if (Screen.hasControlDown()) {\n                    PacketHandler.sendToServer(new PacketBindTool());\n                }\n            }\n        }\n        return new InteractionResultHolder<>(InteractionResult.SUCCESS, itemstack);\n    }\n\n    public void setMode(ItemStack heldItem, int modeInt) {\n        //Called when we specify a mode with the radial menu\n        BuildingModes mode = BuildingModes.values()[modeInt];\n        setToolMode(heldItem, mode);\n    }\n\n    public static void rangeChange(Player player, ItemStack heldItem) {\n        //Called when the range change hotkey is pressed\n        int range = getToolRange(heldItem);\n        int changeAmount = (getToolMode(heldItem) != BuildingModes.SURFACE || (range % 2 == 0)) ? 1 : 2;\n        if (player.isShiftKeyDown())\n            range = (range == 1) ? Config.GADGETS.maxRange.get() : range - changeAmount;\n        else\n            range = (range >= Config.GADGETS.maxRange.get()) ? 1 : range + changeAmount;\n\n        setToolRange(heldItem, range);\n        player.displayClientMessage(MessageTranslation.RANGE_SET.componentTranslation(range).setStyle(Styles.AQUA), true);\n    }\n\n    private void build(ServerPlayer player, ItemStack stack) {\n        //Build the blocks as shown in the visual render\n        Level world = player.level;\n        ItemStack heldItem = getGadget(player);\n        if (heldItem.isEmpty())\n            return;\n\n        List<BlockPos> coords = GadgetUtils.getAnchor(heldItem).orElse(new ArrayList<>());\n\n        BlockData blockData = getToolBlock(heldItem);\n        if (blockData.getState() == Blocks.AIR.defaultBlockState()) {\n            return;\n        }\n\n        if (coords.size() == 0) {  //If we don't have an anchor, build in the current spot\n            BlockHitResult lookingAt = VectorHelper.getLookingAt(player, stack);\n            if (world.isEmptyBlock(lookingAt.getBlockPos())) //If we aren't looking at anything, exit\n                return;\n\n            Direction sideHit = lookingAt.getDirection();\n            coords = getToolMode(stack).getMode().getCollection(\n                    new AbstractMode.UseContext(world, player, blockData.getState(), lookingAt.getBlockPos(), heldItem, sideHit, placeAtop(stack), getConnectedArea(stack)),\n                    player\n            );\n        } else  //If we do have an anchor, erase it (Even if the build fails)\n            setAnchor(stack);\n\n        Undo.Builder builder = Undo.builder();\n        IItemIndex index = InventoryHelper.index(stack, player);\n\n        //TODO replace with a better TileEntity supporting Fake IWorld\n        fakeWorld.setWorldAndState(player.level, blockData.getState(), coords); // Initialize the fake world's blocks\n        for (BlockPos coordinate : coords) {\n            //Get the extended block state in the fake world\n            //Disabled to fix Chisel\n            //state = state.getBlock().getExtendedState(state, fakeWorld, coordinate);\n            placeBlock(world, player, index, builder, coordinate, blockData);\n        }\n\n        pushUndo(stack, builder.build(world));\n    }\n\n    private void placeBlock(Level world, ServerPlayer player, IItemIndex index, Undo.Builder builder, BlockPos pos, BlockData setBlock) {\n        if ((pos.getY() > world.getMaxBuildHeight() || pos.getY() < world.getMinBuildHeight()) || !player.mayBuild())\n            return;\n\n        ItemStack heldItem = getGadget(player);\n        if (heldItem.isEmpty())\n            return;\n\n        boolean useConstructionPaste = false;\n\n        BuildContext buildContext = new BuildContext(world, player, heldItem);\n        MaterialList requiredItems = setBlock.getRequiredItems(buildContext, null, pos);\n\n        // #majorcode\n        MatchResult match = index.tryMatch(requiredItems);\n        if (!match.isSuccess()) {\n            if (setBlock.getState().hasBlockEntity())\n                return;\n            match = index.tryMatch(InventoryHelper.PASTE_LIST);\n            if (!match.isSuccess())\n                return;\n            else\n                useConstructionPaste = true;\n        }\n\n        BlockSnapshot blockSnapshot = BlockSnapshot.create(world.dimension(), world, pos);\n        if (ForgeEventFactory.onBlockPlace(player, blockSnapshot, Direction.UP) || !world.mayInteract(player, pos) || !this.canUse(heldItem, player) || !setBlock.getState().canSurvive(world, pos))\n            return;\n\n        this.applyDamage(heldItem, player);\n\n        if (index.applyMatch(match)) {\n            ImmutableMultiset<IUniqueObject<?>> usedItems = match.getChosenOption();\n            builder.record(world, pos, setBlock, usedItems, ImmutableMultiset.of());\n            EffectBlock.spawnEffectBlock(world, pos, setBlock, EffectBlock.Mode.PLACE, useConstructionPaste);\n        }\n    }\n\n    public static ItemStack getGadget(Player player) {\n        ItemStack stack = AbstractGadget.getGadget(player);\n        if (!(stack.getItem() instanceof GadgetBuilding))\n            return ItemStack.EMPTY;\n        return stack;\n    }\n\n    @Override\n    public boolean performRotate(ItemStack stack, Player player) {\n        GadgetUtils.rotateOrMirrorToolBlock(stack, player, PacketRotateMirror.Operation.ROTATE);\n        return true;\n    }\n\n    @Override\n    public boolean performMirror(ItemStack stack, Player player) {\n        GadgetUtils.rotateOrMirrorToolBlock(stack, player, PacketRotateMirror.Operation.MIRROR);\n        return true;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/items/GadgetCopyPaste.java",
    "content": "package com.direwolf20.buildinggadgets.common.items;\n\nimport com.direwolf20.buildinggadgets.client.events.EventTooltip;\nimport com.direwolf20.buildinggadgets.client.renders.BaseRenderer;\nimport com.direwolf20.buildinggadgets.client.renders.CopyPasteRender;\nimport com.direwolf20.buildinggadgets.client.screen.GuiMod;\nimport com.direwolf20.buildinggadgets.common.BuildingGadgets;\nimport com.direwolf20.buildinggadgets.common.capability.CapabilityTemplate;\nimport com.direwolf20.buildinggadgets.common.capability.provider.TemplateKeyProvider;\nimport com.direwolf20.buildinggadgets.common.commands.ForceUnloadedCommand;\nimport com.direwolf20.buildinggadgets.common.commands.OverrideBuildSizeCommand;\nimport com.direwolf20.buildinggadgets.common.commands.OverrideCopySizeCommand;\nimport com.direwolf20.buildinggadgets.common.config.Config;\nimport com.direwolf20.buildinggadgets.common.network.PacketHandler;\nimport com.direwolf20.buildinggadgets.common.network.packets.PacketBindTool;\nimport com.direwolf20.buildinggadgets.common.tainted.building.PlacementChecker;\nimport com.direwolf20.buildinggadgets.common.tainted.building.Region;\nimport com.direwolf20.buildinggadgets.common.tainted.building.view.BuildContext;\nimport com.direwolf20.buildinggadgets.common.tainted.building.view.IBuildView;\nimport com.direwolf20.buildinggadgets.common.tainted.building.view.WorldBuildView;\nimport com.direwolf20.buildinggadgets.common.tainted.concurrent.CopyScheduler;\nimport com.direwolf20.buildinggadgets.common.tainted.concurrent.PlacementScheduler;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.IItemIndex;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.InventoryHelper;\nimport com.direwolf20.buildinggadgets.common.tainted.save.SaveManager;\nimport com.direwolf20.buildinggadgets.common.tainted.template.ITemplateKey;\nimport com.direwolf20.buildinggadgets.common.tainted.template.Template;\nimport com.direwolf20.buildinggadgets.common.tainted.template.TemplateHeader;\nimport com.direwolf20.buildinggadgets.common.util.Additions;\nimport com.direwolf20.buildinggadgets.common.util.GadgetUtils;\nimport com.direwolf20.buildinggadgets.common.util.exceptions.CapabilityNotPresentException;\nimport com.direwolf20.buildinggadgets.common.util.helpers.VectorHelper;\nimport com.direwolf20.buildinggadgets.common.util.lang.*;\nimport com.direwolf20.buildinggadgets.common.util.ref.NBTKeys;\nimport com.direwolf20.buildinggadgets.common.util.ref.Reference;\nimport com.direwolf20.buildinggadgets.common.util.ref.Reference.BlockReference.TagReference;\nimport com.google.common.base.Joiner;\nimport com.google.common.collect.ImmutableList.Builder;\nimport com.google.common.collect.ImmutableSortedSet;\nimport it.unimi.dsi.fastutil.bytes.Byte2ObjectMap;\nimport it.unimi.dsi.fastutil.bytes.Byte2ObjectOpenHashMap;\nimport net.minecraft.client.gui.screens.Screen;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.nbt.NbtUtils;\nimport net.minecraft.nbt.Tag;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.network.chat.Style;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.minecraft.world.InteractionHand;\nimport net.minecraft.world.InteractionResult;\nimport net.minecraft.world.InteractionResultHolder;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.inventory.tooltip.TooltipComponent;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.TooltipFlag;\nimport net.minecraft.world.item.context.BlockPlaceContext;\nimport net.minecraft.world.item.context.UseOnContext;\nimport net.minecraft.world.level.ChunkPos;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.Blocks;\nimport net.minecraft.world.level.block.Rotation;\nimport net.minecraft.world.level.block.entity.BlockEntity;\nimport net.minecraft.world.phys.BlockHitResult;\nimport net.minecraft.world.phys.HitResult.Type;\nimport net.minecraftforge.common.capabilities.ForgeCapabilities;\nimport net.minecraftforge.common.capabilities.ICapabilityProvider;\nimport net.minecraftforge.network.PacketDistributor;\n\nimport javax.annotation.Nullable;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.function.Supplier;\n\npublic class GadgetCopyPaste extends AbstractGadget {\n\n    public enum ToolMode {\n        COPY(ModeTranslation.COPY, 0),\n        PASTE(ModeTranslation.PASTE, 1);\n        public static final ToolMode[] VALUES = values();\n        private static final Byte2ObjectMap<ToolMode> BY_ID;\n\n        static {\n            BY_ID = new Byte2ObjectOpenHashMap<>();\n            for (ToolMode mode : VALUES) {\n                assert !BY_ID.containsKey(mode.getId());\n                BY_ID.put(mode.getId(), mode);\n            }\n        }\n\n        private final byte id;\n        private final ITranslationProvider translation;\n\n        ToolMode(ITranslationProvider translation, int id) {\n            this.id = (byte) id;\n            this.translation = translation;\n        }\n\n        public byte getId() {\n            return id;\n        }\n\n        @Nullable\n        public static ToolMode ofId(byte id) {\n            return BY_ID.get(id);\n        }\n\n        public ITranslationProvider getTranslation() {\n            return translation;\n        }\n    }\n\n    private static final Joiner CHUNK_JOINER = Joiner.on(\"; \");\n\n    public GadgetCopyPaste() {\n        super(OurItems.nonStackableItemProperties(),\n                Config.GADGETS.GADGET_COPY_PASTE.undoSize::get,\n                Reference.SaveReference.UNDO_COPY_PASTE,\n                TagReference.WHITELIST_COPY_PASTE,\n                TagReference.BLACKLIST_COPY_PASTE);\n    }\n\n    @Override\n    public int getEnergyMax() {\n        return Config.GADGETS.GADGET_COPY_PASTE.maxEnergy.get();\n    }\n\n    @Override\n    public int getEnergyCost(ItemStack tool) {\n        return Config.GADGETS.GADGET_COPY_PASTE.energyCost.get();\n    }\n\n    @Override\n    protected Supplier<BaseRenderer> createRenderFactory() {\n        return CopyPasteRender::new;\n    }\n\n    @Override\n    public CopyPasteRender getRender() {\n        return (CopyPasteRender) super.getRender();\n    }\n\n    @Override\n    protected void addCapabilityProviders(Builder<ICapabilityProvider> providerBuilder, ItemStack stack, @Nullable CompoundTag tag) {\n        super.addCapabilityProviders(providerBuilder, stack, tag);\n        providerBuilder.add(new TemplateKeyProvider(stack));\n    }\n\n    @Override\n    public boolean performRotate(ItemStack stack, Player player) {\n        return player.level.getCapability(CapabilityTemplate.TEMPLATE_PROVIDER_CAPABILITY).map(provider ->\n                        stack.getCapability(CapabilityTemplate.TEMPLATE_KEY_CAPABILITY).map(key -> {\n                            Template template = provider.getTemplateForKey(key);\n                            provider.setTemplate(key, template.rotate(Rotation.CLOCKWISE_90));\n                            provider.requestRemoteUpdate(key, PacketDistributor.PLAYER.with(() -> (ServerPlayer) player));\n                            return true;\n                        }).orElse(false))\n                .orElse(false);\n    }\n\n    @Override\n    public boolean performMirror(ItemStack stack, Player player) {\n        return player.level.getCapability(CapabilityTemplate.TEMPLATE_PROVIDER_CAPABILITY).map(provider ->\n                        stack.getCapability(CapabilityTemplate.TEMPLATE_KEY_CAPABILITY).map(key -> {\n                            Template template = provider.getTemplateForKey(key);\n                            provider.setTemplate(key, template.mirror(player.getDirection().getAxis()));\n                            provider.requestRemoteUpdate(key, PacketDistributor.PLAYER.with(() -> (ServerPlayer) player));\n                            return true;\n                        }).orElse(false))\n                .orElse(false);\n    }\n\n    public static void setRelativeVector(ItemStack stack, BlockPos vec) {\n        CompoundTag nbt = stack.getOrCreateTag();\n        if (vec.equals(BlockPos.ZERO))\n            nbt.remove(NBTKeys.GADGET_REL_POS);\n        else\n            nbt.put(NBTKeys.GADGET_REL_POS, NbtUtils.writeBlockPos(vec));\n    }\n\n    public static BlockPos getRelativeVector(ItemStack stack) {\n        CompoundTag nbt = stack.getOrCreateTag();\n        //if not present, then this will just return (0, 0, 0)\n        return NbtUtils.readBlockPos(nbt.getCompound(NBTKeys.GADGET_REL_POS));\n    }\n\n    public static int getCopyCounter(ItemStack stack) {\n        CompoundTag nbt = stack.getOrCreateTag();\n        return nbt.getInt(NBTKeys.TEMPLATE_COPY_COUNT); //returns 0 if not present\n    }\n\n    public static int getAndIncrementCopyCounter(ItemStack stack) {\n        CompoundTag nbt = stack.getOrCreateTag();\n        int count = nbt.getInt(NBTKeys.TEMPLATE_COPY_COUNT); //returns 0 if not present\n        nbt.putInt(NBTKeys.TEMPLATE_COPY_COUNT, count + 1);\n        return count;\n    }\n\n    public static Optional<BlockPos> getActivePos(Player playerEntity, ItemStack stack) {\n        BlockPos pos = ((AbstractGadget) stack.getItem()).getAnchor(stack);\n        if (pos == null) {\n            BlockHitResult res = VectorHelper.getLookingAt(playerEntity, stack);\n            if (res == null || res.getType() == Type.MISS)\n                return Optional.empty();\n            pos = res.getBlockPos().relative(res.getDirection());\n        }\n        return Optional.of(pos).map(p -> p.offset(getRelativeVector(stack)));\n    }\n\n    public static Optional<Region> getSelectedRegion(ItemStack stack) {\n        BlockPos lower = getLowerRegionBound(stack);\n        BlockPos upper = getUpperRegionBound(stack);\n        if (lower != null && upper != null)\n            return Optional.of(new Region(lower, upper));\n        return Optional.empty();\n    }\n\n    public static void setSelectedRegion(ItemStack stack, @Nullable Region region) {\n        if (region != null) {\n            setLowerRegionBound(stack, region.getMin());\n            setUpperRegionBound(stack, region.getMax());\n        } else {\n            setLowerRegionBound(stack, null);\n            setUpperRegionBound(stack, null);\n        }\n    }\n\n    public static void setUpperRegionBound(ItemStack stack, @Nullable BlockPos pos) {\n        CompoundTag nbt = stack.getOrCreateTag();\n        if (pos != null)\n            nbt.put(NBTKeys.GADGET_START_POS, NbtUtils.writeBlockPos(pos));\n        else\n            nbt.remove(NBTKeys.GADGET_START_POS);\n    }\n\n    public static void setLowerRegionBound(ItemStack stack, @Nullable BlockPos pos) {\n        CompoundTag nbt = stack.getOrCreateTag();\n        if (pos != null)\n            nbt.put(NBTKeys.GADGET_END_POS, NbtUtils.writeBlockPos(pos));\n        else\n            nbt.remove(NBTKeys.GADGET_END_POS);\n    }\n\n    @Nullable\n    public static BlockPos getUpperRegionBound(ItemStack stack) {\n        CompoundTag nbt = stack.getOrCreateTag();\n        if (nbt.contains(NBTKeys.GADGET_START_POS, Tag.TAG_COMPOUND))\n            return NbtUtils.readBlockPos(nbt.getCompound(NBTKeys.GADGET_START_POS));\n        return null;\n    }\n\n    @Nullable\n    public static BlockPos getLowerRegionBound(ItemStack stack) {\n        CompoundTag nbt = stack.getOrCreateTag();\n        if (nbt.contains(NBTKeys.GADGET_END_POS, Tag.TAG_COMPOUND))\n            return NbtUtils.readBlockPos(nbt.getCompound(NBTKeys.GADGET_END_POS));\n        return null;\n    }\n\n    private static void setToolMode(ItemStack stack, ToolMode mode) {\n        CompoundTag tagCompound = stack.getOrCreateTag();\n        tagCompound.putByte(NBTKeys.GADGET_MODE, mode.getId());\n    }\n\n    public static ToolMode getToolMode(ItemStack stack) {\n        CompoundTag tagCompound = stack.getOrCreateTag();\n        ToolMode mode = ToolMode.COPY;\n        if (!tagCompound.contains(NBTKeys.GADGET_MODE, Tag.TAG_BYTE)) {\n            setToolMode(stack, mode);\n            return mode;\n        }\n        mode = ToolMode.ofId(tagCompound.getByte(NBTKeys.GADGET_MODE));\n        if (mode == null) {\n            BuildingGadgets.LOG.debug(\"Failed to read Tool Mode {} falling back to {}.\", tagCompound.getString(NBTKeys.GADGET_MODE), mode);\n            mode = ToolMode.COPY;\n            setToolMode(stack, mode);\n        }\n        return mode;\n    }\n\n    @Override\n    protected void onAnchorSet(ItemStack stack, Player player, BlockHitResult lookingAt) {\n        //offset by one\n        super.onAnchorSet(stack, player, new BlockHitResult(lookingAt.getLocation(), lookingAt.getDirection(), lookingAt.getBlockPos().relative(lookingAt.getDirection()), lookingAt.isInside()));\n    }\n\n    public static ItemStack getGadget(Player player) {\n        ItemStack stack = AbstractGadget.getGadget(player);\n        if (!(stack.getItem() instanceof GadgetCopyPaste))\n            return ItemStack.EMPTY;\n\n        return stack;\n    }\n\n    @Override\n    public void appendHoverText(ItemStack stack, @Nullable Level world, List<Component> tooltip, TooltipFlag flag) {\n        super.appendHoverText(stack, world, tooltip, flag);\n\n        addEnergyInformation(tooltip, stack);\n\n        tooltip.add(TooltipTranslation.GADGET_MODE.componentTranslation(getToolMode(stack).translation.format()).setStyle(Styles.AQUA));\n        tooltip.add(Component.literal(\"My renders don't really work yet, outlines for now :D\").setStyle(Styles.GRAY));\n\n        addInformationRayTraceFluid(tooltip, stack);\n        GadgetUtils.addTooltipNameAndAuthor(stack, world, tooltip);\n    }\n\n    public void setMode(ItemStack heldItem, int modeInt) {\n        // Called when we specify a mode with the radial menu\n        ToolMode mode = ToolMode.values()[modeInt];\n        setToolMode(heldItem, mode);\n    }\n\n    @Override\n    public InteractionResultHolder<ItemStack> use(Level world, Player player, InteractionHand hand) {\n        ItemStack stack = player.getItemInHand(hand);\n        player.startUsingItem(hand);\n\n        BlockHitResult posLookingAt = VectorHelper.getLookingAt(player, stack);\n        BlockEntity tileEntity = world.getBlockEntity(posLookingAt.getBlockPos());\n        boolean lookingAtInventory = tileEntity != null && tileEntity.getCapability(ForgeCapabilities.ITEM_HANDLER).isPresent();\n\n        if (!world.isClientSide()) {\n            if (player.isShiftKeyDown() && lookingAtInventory) {\n                return InteractionResultHolder.pass(stack);\n            }\n\n            if (getToolMode(stack) == ToolMode.COPY) {\n                if (world.getBlockState(posLookingAt.getBlockPos()) != Blocks.AIR.defaultBlockState())\n                    setRegionAndCopy(stack, world, player, posLookingAt.getBlockPos());\n            } else if (getToolMode(stack) == ToolMode.PASTE && !player.isShiftKeyDown())\n                getActivePos(player, stack).ifPresent(pos -> build(stack, world, player, pos));\n        } else {\n            if (player.isShiftKeyDown() && Screen.hasControlDown() && lookingAtInventory) {\n                PacketHandler.sendToServer(new PacketBindTool());\n                return InteractionResultHolder.pass(stack);\n            }\n\n            if (getToolMode(stack) == ToolMode.COPY) {\n                if (player.isShiftKeyDown() && world.getBlockState(posLookingAt.getBlockPos()) == Blocks.AIR.defaultBlockState())\n                    GuiMod.COPY.openScreen(player);\n            } else if (player.isShiftKeyDown()) {\n                GuiMod.PASTE.openScreen(player);\n            } else {\n                BaseRenderer.updateInventoryCache();\n            }\n        }\n        return new InteractionResultHolder<>(InteractionResult.SUCCESS, stack);\n    }\n\n    @Override\n    public Optional<TooltipComponent> getTooltipImage(ItemStack p_150902_) {\n        return Optional.of(new EventTooltip.CopyPasteTooltipComponent.Data(p_150902_));\n    }\n\n    private void setRegionAndCopy(ItemStack stack, Level world, Player player, BlockPos lookedAt) {\n        if (player.isShiftKeyDown()) {\n            if (getLowerRegionBound(stack) != null && !checkCopy(world, player, new Region(lookedAt, getLowerRegionBound(stack))))\n                return;\n            setUpperRegionBound(stack, lookedAt);\n        } else {\n            if (getUpperRegionBound(stack) != null && !checkCopy(world, player, new Region(lookedAt, getUpperRegionBound(stack))))\n                return;\n            setLowerRegionBound(stack, lookedAt);\n        }\n        Optional<Region> regionOpt = getSelectedRegion(stack);\n        if (!regionOpt.isPresent()) //notify of single copy\n            player.displayClientMessage(MessageTranslation.FIRST_COPY.componentTranslation().setStyle(Styles.DK_GREEN), true);\n        regionOpt.ifPresent(region -> tryCopy(stack, world, player, region));\n    }\n\n    public void tryCopy(ItemStack stack, Level world, Player player, Region region) {\n        BuildContext context = BuildContext.builder()\n                .player(player)\n                .stack(stack)\n                .build(world);\n        WorldBuildView buildView = WorldBuildView.create(context, region,\n                (c, p) -> InventoryHelper.getSafeBlockData(player, p, player.getUsedItemHand()));\n        performCopy(stack, buildView);\n    }\n\n    private boolean checkCopy(Level world, Player player, Region region) {\n        if (!ForceUnloadedCommand.mayForceUnloadedChunks(player)) {\n            ImmutableSortedSet<ChunkPos> unloaded = region.getUnloadedChunks(world);\n            if (!unloaded.isEmpty()) {\n                player.displayClientMessage(MessageTranslation.COPY_UNLOADED.componentTranslation(unloaded.size()).setStyle(Styles.RED), true);\n                BuildingGadgets.LOG.debug(\"Prevented copy because {} chunks where detected as unloaded.\", unloaded.size());\n                BuildingGadgets.LOG.trace(\"The following chunks were detected as unloaded {}.\", CHUNK_JOINER.join(unloaded));\n                return false;\n            }\n        }\n        int maxDimension = Config.GADGETS.GADGET_COPY_PASTE.maxCopySize.get();\n        if (region.getXSize() > 0xFFFF || region.getYSize() > 255 || region.getZSize() > 0xFFFF ||  //these are the max dimensions of a Template\n                ((region.getXSize() > maxDimension || region.getYSize() > maxDimension || region.getZSize() > maxDimension) && !OverrideCopySizeCommand.mayPerformLargeCopy(player))) {\n            BlockPos sizeVec = region.getMax().subtract(region.getMin());\n            player.displayClientMessage(MessageTranslation.COPY_TOO_LARGE\n                    .componentTranslation(sizeVec.getX(), sizeVec.getY(), sizeVec.getZ(), Math.min(maxDimension, 0xFFFF), Math.min(maxDimension, 255), Math.min(maxDimension, 0xFFFF))\n                    .setStyle(Styles.RED), true);\n            return false;\n        }\n        return true;\n    }\n\n    private void performCopy(ItemStack stack, WorldBuildView buildView) {\n        BuildContext context = buildView.getContext();\n        assert context.getPlayer() != null;\n        Player player = context.getPlayer();\n        CopyScheduler.scheduleCopy((map, region) -> {\n            Template newTemplate = new Template(map,\n                    TemplateHeader.builder(region)\n                            .name(\"Copy \" + getAndIncrementCopyCounter(stack))\n                            .author(player.getName().getString())\n                            .build());\n            onCopyFinished(newTemplate.normalize(), stack, player);\n        }, buildView, Config.GADGETS.GADGET_COPY_PASTE.copySteps.get());\n    }\n\n    private void onCopyFinished(Template newTemplate, ItemStack stack, Player player) {\n        if (!Additions.sizeInvalid(player, newTemplate.getHeader().getBoundingBox()))\n            sendMessage(stack, player, MessageTranslation.AREA_COPIED, Styles.DK_GREEN);\n        ITemplateKey key = stack.getCapability(CapabilityTemplate.TEMPLATE_KEY_CAPABILITY).orElseThrow(CapabilityNotPresentException::new);\n        SaveManager.INSTANCE.getTemplateProvider().setTemplate(key, newTemplate);\n        SaveManager.INSTANCE.getTemplateProvider().requestRemoteUpdate(key, (ServerPlayer) player);\n    }\n\n    private void build(ItemStack stack, Level world, Player player, BlockPos pos) {\n        world.getCapability(CapabilityTemplate.TEMPLATE_PROVIDER_CAPABILITY).ifPresent(provider -> {\n            stack.getCapability(CapabilityTemplate.TEMPLATE_KEY_CAPABILITY).ifPresent(key -> {\n                Template template = provider.getTemplateForKey(key);\n                BuildContext buildContext = BuildContext.builder()\n                        .stack(stack)\n                        .player(player)\n                        .build(world);\n                IBuildView view = template.createViewInContext(buildContext);\n                view.translateTo(pos);\n                if (!checkPlacement(world, player, view.getBoundingBox()))\n                    return;\n                schedulePlacement(stack, view, player);\n            });\n        });\n    }\n\n    private boolean checkPlacement(Level world, Player player, Region region) {\n        if (!ForceUnloadedCommand.mayForceUnloadedChunks(player)) {\n            ImmutableSortedSet<ChunkPos> unloaded = region.getUnloadedChunks(world);\n            if (!unloaded.isEmpty()) {\n                player.displayClientMessage(MessageTranslation.BUILD_UNLOADED.componentTranslation(unloaded.size()).setStyle(Styles.RED), true);\n                BuildingGadgets.LOG.debug(\"Prevented build because {} chunks where detected as unloaded.\", unloaded.size());\n                BuildingGadgets.LOG.trace(\"The following chunks were detected as unloaded {}.\", CHUNK_JOINER.join(unloaded));\n                return false;\n            }\n        }\n        int maxDimension = Config.GADGETS.GADGET_COPY_PASTE.maxBuildSize.get();\n        if ((region.getXSize() > maxDimension || region.getYSize() > maxDimension || region.getZSize() > maxDimension) &&\n                !OverrideBuildSizeCommand.mayPerformLargeBuild(player)) {\n            BlockPos sizeVec = region.getMax().subtract(region.getMin());\n            player.displayClientMessage(MessageTranslation.BUILD_TOO_LARGE\n                    .componentTranslation(sizeVec.getX(), sizeVec.getY(), sizeVec.getZ(), maxDimension, maxDimension, maxDimension)\n                    .setStyle(Styles.RED), true);\n            return false;\n        }\n        return true;\n    }\n\n    private void schedulePlacement(ItemStack stack, IBuildView view, Player player) {\n        IItemIndex index = InventoryHelper.index(stack, player);\n        // Disable energy cost when max energy is disabled\n        int energyCost = getEnergyMax() == 0 ? 0 : getEnergyCost(stack);\n        boolean overwrite = Config.GENERAL.allowOverwriteBlocks.get();\n        BlockPlaceContext useContext = new BlockPlaceContext(new UseOnContext(player, InteractionHand.MAIN_HAND, VectorHelper.getLookingAt(player, stack)));\n        PlacementChecker checker = new PlacementChecker(\n                stack.getCapability(ForgeCapabilities.ENERGY),\n                t -> energyCost,\n                index,\n                (c, t) -> overwrite ? c.getWorld().getBlockState(t.getPos()).canBeReplaced(useContext) : c.getWorld().isEmptyBlock(t.getPos()),\n                true);\n        PlacementScheduler.schedulePlacement(view, checker, Config.GADGETS.placeSteps.get())\n                .withFinisher(p -> {\n                    pushUndo(stack, p.getUndoBuilder().build(view.getContext().getServerWorld()));\n                    onBuildFinished(stack, player, view.getBoundingBox());\n                });\n    }\n\n    private void onBuildFinished(ItemStack stack, Player player, Region bounds) {\n        if (!Additions.sizeInvalid(player, bounds))\n            sendMessage(stack, player, MessageTranslation.TEMPLATE_BUILD, Styles.DK_GREEN);\n    }\n\n    private void sendMessage(ItemStack stack, Player player, ITranslationProvider messageSource, Style style) {\n        player.displayClientMessage(messageSource.componentTranslation().setStyle(style), true);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/items/GadgetDestruction.java",
    "content": "package com.direwolf20.buildinggadgets.common.items;\n\nimport com.direwolf20.buildinggadgets.client.renders.BaseRenderer;\nimport com.direwolf20.buildinggadgets.client.renders.DestructionRender;\nimport com.direwolf20.buildinggadgets.client.screen.GuiMod;\nimport com.direwolf20.buildinggadgets.common.blocks.EffectBlock;\nimport com.direwolf20.buildinggadgets.common.blocks.OurBlocks;\nimport com.direwolf20.buildinggadgets.common.config.Config;\nimport com.direwolf20.buildinggadgets.common.tainted.building.BlockData;\nimport com.direwolf20.buildinggadgets.common.tainted.building.Region;\nimport com.direwolf20.buildinggadgets.common.tainted.building.tilesupport.TileSupport;\nimport com.direwolf20.buildinggadgets.common.tainted.save.Undo;\nimport com.direwolf20.buildinggadgets.common.tileentities.ConstructionBlockTileEntity;\nimport com.direwolf20.buildinggadgets.common.util.GadgetUtils;\nimport com.direwolf20.buildinggadgets.common.util.helpers.VectorHelper;\nimport com.direwolf20.buildinggadgets.common.util.lang.Styles;\nimport com.direwolf20.buildinggadgets.common.util.lang.TooltipTranslation;\nimport com.direwolf20.buildinggadgets.common.util.ref.NBTKeys;\nimport com.direwolf20.buildinggadgets.common.util.ref.Reference;\nimport com.direwolf20.buildinggadgets.common.util.ref.Reference.BlockReference.TagReference;\nimport com.direwolf20.buildinggadgets.common.util.tools.RegistryUtils;\nimport com.google.common.collect.ImmutableMultiset;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.minecraft.world.InteractionHand;\nimport net.minecraft.world.InteractionResult;\nimport net.minecraft.world.InteractionResultHolder;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.TooltipFlag;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.entity.BlockEntity;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.phys.BlockHitResult;\nimport net.minecraftforge.common.MinecraftForge;\nimport net.minecraftforge.common.util.BlockSnapshot;\nimport net.minecraftforge.event.ForgeEventFactory;\nimport net.minecraftforge.event.level.BlockEvent;\nimport net.minecraftforge.registries.ForgeRegistries;\n\nimport javax.annotation.Nullable;\nimport java.util.ArrayList;\nimport java.util.Comparator;\nimport java.util.List;\nimport java.util.function.Supplier;\nimport java.util.stream.Collectors;\n\npublic class GadgetDestruction extends AbstractGadget {\n\n    public GadgetDestruction() {\n        super(OurItems.nonStackableItemProperties(),\n                Config.GADGETS.GADGET_DESTRUCTION.undoSize::get,\n                Reference.SaveReference.UNDO_DESTRUCTION,\n                TagReference.WHITELIST_DESTRUCTION,\n                TagReference.BLACKLIST_DESTRUCTION);\n    }\n\n    @Override\n    public int getEnergyMax() {\n        return Config.GADGETS.GADGET_DESTRUCTION.maxEnergy.get();\n    }\n\n    @Override\n    public int getEnergyCost(ItemStack tool) {\n        return Config.GADGETS.GADGET_DESTRUCTION.energyCost.get() * getCostMultiplier(tool);\n    }\n\n    @Override\n    protected Supplier<BaseRenderer> createRenderFactory() {\n        return DestructionRender::new;\n    }\n\n    private int getCostMultiplier(ItemStack tool) {\n        return (int) (!getFuzzy(tool) ? Config.GADGETS.GADGET_DESTRUCTION.nonFuzzyMultiplier.get() : 1);\n    }\n\n    @Override\n    public void appendHoverText(ItemStack stack, @Nullable Level world, List<Component> tooltip, TooltipFlag flag) {\n        super.appendHoverText(stack, world, tooltip, flag);\n        addEnergyInformation(tooltip, stack);\n\n        tooltip.add(TooltipTranslation.GADGET_DESTROYWARNING\n                .componentTranslation()\n                .setStyle(Styles.RED));\n\n        tooltip.add(TooltipTranslation.GADGET_DESTROYSHOWOVERLAY\n                .componentTranslation(String.valueOf(getOverlay(stack)))\n                .setStyle(Styles.AQUA));\n\n        tooltip.add(TooltipTranslation.GADGET_BUILDING_PLACE_ATOP\n                .componentTranslation(String.valueOf(getConnectedArea(stack)))\n                .setStyle(Styles.YELLOW));\n\n        if (Config.GADGETS.GADGET_DESTRUCTION.nonFuzzyEnabled.get())\n            tooltip.add(TooltipTranslation.GADGET_FUZZY\n                    .componentTranslation(String.valueOf(getFuzzy(stack)))\n                    .setStyle(Styles.GOLD));\n\n        addInformationRayTraceFluid(tooltip, stack);\n    }\n\n    public static void setAnchor(ItemStack stack, BlockPos pos) {\n        GadgetUtils.writePOSToNBT(stack, pos, NBTKeys.GADGET_ANCHOR);\n    }\n\n    public static void setAnchorSide(ItemStack stack, Direction side) {\n        CompoundTag tag = stack.getOrCreateTag();\n        if (side == null)\n            tag.remove(NBTKeys.GADGET_ANCHOR_SIDE);\n        else\n            tag.putString(NBTKeys.GADGET_ANCHOR_SIDE, side.getName());\n    }\n\n    public static Direction getAnchorSide(ItemStack stack) {\n        CompoundTag tag = stack.getOrCreateTag();\n        String facing = tag.getString(NBTKeys.GADGET_ANCHOR_SIDE);\n        if (facing.isEmpty())\n            return null;\n        return Direction.byName(facing);\n    }\n\n    public static void setToolValue(ItemStack stack, int value, String valueName) {\n        stack.getOrCreateTag().putInt(valueName, value);\n    }\n\n    public static int getToolValue(ItemStack stack, String valueName) {\n        return stack.getOrCreateTag().getInt(valueName);\n    }\n\n    public static boolean getOverlay(ItemStack stack) {\n        CompoundTag tag = stack.getOrCreateTag();\n        if (tag.contains(NBTKeys.GADGET_OVERLAY))\n            return tag.getBoolean(NBTKeys.GADGET_OVERLAY);\n\n        tag.putBoolean(NBTKeys.GADGET_OVERLAY, true);\n        tag.putBoolean(NBTKeys.GADGET_FUZZY, true); // We want a Destruction Gadget to start with fuzzy=true\n        return true;\n    }\n\n    public static void setOverlay(ItemStack stack, boolean showOverlay) {\n        stack.getOrCreateTag().putBoolean(NBTKeys.GADGET_OVERLAY, showOverlay);\n    }\n\n    public static void switchOverlay(Player player, ItemStack stack) {\n        boolean newOverlay = !getOverlay(stack);\n        setOverlay(stack, newOverlay);\n        player.displayClientMessage(TooltipTranslation.GADGET_DESTROYSHOWOVERLAY\n                .componentTranslation(newOverlay).setStyle(Styles.AQUA), true);\n    }\n\n    public static boolean getIsFluidOnly(ItemStack stack) {\n        return stack.getOrCreateTag().getBoolean(NBTKeys.GADGET_FLUID_ONLY);\n    }\n\n    public static void toggleFluidMode(ItemStack stack) {\n        stack.getOrCreateTag().putBoolean(NBTKeys.GADGET_FLUID_ONLY, !getIsFluidOnly(stack));\n    }\n\n    @Override\n    public InteractionResultHolder<ItemStack> use(Level world, Player player, InteractionHand hand) {\n        ItemStack stack = player.getItemInHand(hand);\n        player.startUsingItem(hand);\n\n        if (!world.isClientSide) {\n            if (!player.isShiftKeyDown()) {\n                BlockPos anchorPos = getAnchor(stack);\n                Direction anchorSide = getAnchorSide(stack);\n                if (anchorPos != null && anchorSide != null) {\n                    clearArea(world, anchorPos, anchorSide, (ServerPlayer) player, stack);\n                    onAnchorRemoved(stack, player);\n                    return new InteractionResultHolder<>(InteractionResult.SUCCESS, stack);\n                }\n\n                BlockHitResult lookingAt = VectorHelper.getLookingAt(player, stack);\n                if (!world.isEmptyBlock(lookingAt.getBlockPos())) {\n                    clearArea(world, lookingAt.getBlockPos(), lookingAt.getDirection(), (ServerPlayer) player, stack);\n                    onAnchorRemoved(stack, player);\n                    return new InteractionResultHolder<>(InteractionResult.SUCCESS, stack);\n                }\n\n                return new InteractionResultHolder<>(InteractionResult.FAIL, stack);\n            }\n        } else if (player.isShiftKeyDown()) {\n            GuiMod.DESTRUCTION.openScreen(player);\n        }\n        return new InteractionResultHolder<>(InteractionResult.SUCCESS, stack);\n    }\n\n    @Override\n    protected void onAnchorSet(ItemStack stack, Player player, BlockHitResult lookingAt) {\n        super.onAnchorSet(stack, player, lookingAt);\n        setAnchorSide(stack, lookingAt.getDirection());\n    }\n\n    @Override\n    protected void onAnchorRemoved(ItemStack stack, Player player) {\n        super.onAnchorRemoved(stack, player);\n        setAnchorSide(stack, null);\n    }\n\n    public static List<BlockPos> getArea(Level world, BlockPos pos, Direction incomingSide, Player player, ItemStack stack) {\n        ItemStack tool = getGadget(player);\n        int depth = getToolValue(stack, NBTKeys.GADGET_VALUE_DEPTH);\n\n        if (tool.isEmpty() || depth == 0 || !player.mayBuild())\n            return new ArrayList<>();\n\n        boolean vertical = incomingSide.getAxis().isVertical();\n        Direction up = vertical ? player.getDirection() : Direction.UP;\n        Direction down = up.getOpposite();\n        Direction right = vertical ? up.getClockWise() : incomingSide.getCounterClockWise();\n        Direction left = right.getOpposite();\n\n        BlockPos first = pos.relative(left, getToolValue(stack, NBTKeys.GADGET_VALUE_LEFT)).relative(up, getToolValue(stack, NBTKeys.GADGET_VALUE_UP));\n        BlockPos second = pos.relative(right, getToolValue(stack, NBTKeys.GADGET_VALUE_RIGHT))\n                .relative(down, getToolValue(stack, NBTKeys.GADGET_VALUE_DOWN))\n                .relative(incomingSide.getOpposite(), depth - 1);\n\n        boolean isFluidOnly = getIsFluidOnly(stack);\n        return new Region(first, second).stream()\n                .filter(e ->\n                        isFluidOnly\n                                ? isFluidBlock(world, e)\n                                : isValidBlock(world, e, player, world.getBlockState(e))\n                )\n                .sorted(Comparator.comparing(player.blockPosition()::distSqr))\n                .collect(Collectors.toList());\n    }\n\n    public static boolean isFluidBlock(Level world, BlockPos pos) {\n        if (world.getFluidState(pos).isEmpty()) {\n            return false;\n        }\n\n        return ForgeRegistries.FLUIDS.containsKey(RegistryUtils.getBlockId(world.getBlockState(pos).getBlock()));\n    }\n\n    public static boolean isValidBlock(Level world, BlockPos voidPos, Player player, BlockState currentBlock) {\n        if (world.isEmptyBlock(voidPos) ||\n                currentBlock.equals(OurBlocks.EFFECT_BLOCK.get().defaultBlockState()) ||\n                currentBlock.getDestroySpeed(world, voidPos) < 0 ||\n                !world.mayInteract(player, voidPos)) return false;\n\n        BlockEntity te = world.getBlockEntity(voidPos);\n        if ((te != null) && !(te instanceof ConstructionBlockTileEntity))\n            return false;\n\n        if (!world.isClientSide) {\n            BlockSnapshot blockSnapshot = BlockSnapshot.create(world.dimension(), world, voidPos);\n            if (ForgeEventFactory.onBlockPlace(player, blockSnapshot, Direction.UP))\n                return false;\n            BlockEvent.BreakEvent e = new BlockEvent.BreakEvent(world, voidPos, currentBlock, player);\n            return !MinecraftForge.EVENT_BUS.post(e);\n        }\n        return true;\n    }\n\n    public void clearArea(Level world, BlockPos pos, Direction side, ServerPlayer player, ItemStack stack) {\n        List<BlockPos> positions = getArea(world, pos, side, player, stack);\n        Undo.Builder builder = Undo.builder();\n\n        for (BlockPos clearPos : positions) {\n            BlockState state = world.getBlockState(clearPos);\n            BlockEntity te = world.getBlockEntity(clearPos);\n            if (!isAllowedBlock(state))\n                continue;\n            if (te == null || state.getBlock() == OurBlocks.CONSTRUCTION_BLOCK.get() && te instanceof ConstructionBlockTileEntity) {\n                destroyBlock(world, clearPos, player, builder);\n            }\n        }\n\n        pushUndo(stack, builder.build(world));\n    }\n\n    private boolean destroyBlock(Level world, BlockPos voidPos, ServerPlayer player, Undo.Builder builder) {\n        if (world.isEmptyBlock(voidPos))\n            return false;\n\n        ItemStack tool = getGadget(player);\n        if (tool.isEmpty())\n            return false;\n\n        if (!this.canUse(tool, player))\n            return false;\n\n        this.applyDamage(tool, player);\n        builder.record(world, voidPos, BlockData.AIR, ImmutableMultiset.of(), ImmutableMultiset.of());\n        EffectBlock.spawnEffectBlock(world, voidPos, TileSupport.createBlockData(world, voidPos), EffectBlock.Mode.REMOVE, false);\n        return true;\n    }\n\n    public static ItemStack getGadget(Player player) {\n        ItemStack stack = AbstractGadget.getGadget(player);\n        if (!(stack.getItem() instanceof GadgetDestruction))\n            return ItemStack.EMPTY;\n\n        return stack;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/items/GadgetExchanger.java",
    "content": "package com.direwolf20.buildinggadgets.common.items;\n\nimport com.direwolf20.buildinggadgets.client.renders.BaseRenderer;\nimport com.direwolf20.buildinggadgets.client.renders.BuildRender;\nimport com.direwolf20.buildinggadgets.common.blocks.EffectBlock;\nimport com.direwolf20.buildinggadgets.common.blocks.OurBlocks;\nimport com.direwolf20.buildinggadgets.common.config.Config;\nimport com.direwolf20.buildinggadgets.common.items.modes.AbstractMode;\nimport com.direwolf20.buildinggadgets.common.items.modes.ExchangingModes;\nimport com.direwolf20.buildinggadgets.common.network.PacketHandler;\nimport com.direwolf20.buildinggadgets.common.network.packets.PacketBindTool;\nimport com.direwolf20.buildinggadgets.common.network.packets.PacketRotateMirror;\nimport com.direwolf20.buildinggadgets.common.tainted.building.BlockData;\nimport com.direwolf20.buildinggadgets.common.tainted.building.tilesupport.ITileEntityData;\nimport com.direwolf20.buildinggadgets.common.tainted.building.tilesupport.TileSupport;\nimport com.direwolf20.buildinggadgets.common.tainted.building.view.BuildContext;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.IItemIndex;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.InventoryHelper;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.MatchResult;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.materials.MaterialList;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.materials.objects.IUniqueObject;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.materials.objects.UniqueItem;\nimport com.direwolf20.buildinggadgets.common.tileentities.ConstructionBlockTileEntity;\nimport com.direwolf20.buildinggadgets.common.util.GadgetUtils;\nimport com.direwolf20.buildinggadgets.common.util.helpers.VectorHelper;\nimport com.direwolf20.buildinggadgets.common.util.lang.LangUtil;\nimport com.direwolf20.buildinggadgets.common.util.lang.MessageTranslation;\nimport com.direwolf20.buildinggadgets.common.util.lang.Styles;\nimport com.direwolf20.buildinggadgets.common.util.lang.TooltipTranslation;\nimport com.direwolf20.buildinggadgets.common.util.ref.Reference.BlockReference.TagReference;\nimport com.direwolf20.buildinggadgets.common.util.tools.RegistryUtils;\nimport com.direwolf20.buildinggadgets.common.world.MockBuilderWorld;\nimport com.google.common.collect.ImmutableMultiset;\nimport com.google.common.collect.LinkedHashMultiset;\nimport com.google.common.collect.Multiset;\nimport net.minecraft.client.gui.screens.Screen;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.server.level.ServerLevel;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.minecraft.world.InteractionHand;\nimport net.minecraft.world.InteractionResult;\nimport net.minecraft.world.InteractionResultHolder;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.TooltipFlag;\nimport net.minecraft.world.item.enchantment.Enchantment;\nimport net.minecraft.world.item.enchantment.EnchantmentHelper;\nimport net.minecraft.world.item.enchantment.Enchantments;\nimport net.minecraft.world.level.ClipContext;\nimport net.minecraft.world.level.ClipContext.Fluid;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.Block;\nimport net.minecraft.world.level.block.Blocks;\nimport net.minecraft.world.level.block.entity.BlockEntity;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.phys.BlockHitResult;\nimport net.minecraft.world.phys.Vec3;\nimport net.minecraftforge.common.MinecraftForge;\nimport net.minecraftforge.common.util.BlockSnapshot;\nimport net.minecraftforge.event.ForgeEventFactory;\nimport net.minecraftforge.event.level.BlockEvent;\n\nimport javax.annotation.Nullable;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.function.Supplier;\nimport java.util.stream.Collectors;\n\nimport static com.direwolf20.buildinggadgets.common.util.GadgetUtils.*;\n\npublic class GadgetExchanger extends AbstractGadget {\n    private static final MockBuilderWorld fakeWorld = new MockBuilderWorld();\n\n    public GadgetExchanger() {\n        super(OurItems.nonStackableItemProperties(),\n                () -> 0,\n                \"\",\n                TagReference.WHITELIST_EXCHANGING,\n                TagReference.BLACKLIST_EXCHANGING);\n    }\n\n    @Override\n    public int getEnergyMax() {\n        return Config.GADGETS.GADGET_EXCHANGER.maxEnergy.get();\n    }\n\n    @Override\n    public int getEnergyCost(ItemStack tool) {\n        return Config.GADGETS.GADGET_EXCHANGER.energyCost.get();\n    }\n\n    @Override\n    protected Supplier<BaseRenderer> createRenderFactory() {\n        return () -> new BuildRender(true);\n    }\n\n    @Override\n    public int getEnchantmentValue() {\n        return 3;\n    }\n\n    @Override\n    public boolean isEnchantable(ItemStack stack) {\n        return true;\n    }\n\n    @Override\n    public boolean isBookEnchantable(ItemStack stack, ItemStack book) {\n        return EnchantmentHelper.getEnchantments(book).containsKey(Enchantments.SILK_TOUCH) || super.isBookEnchantable(stack, book);\n    }\n\n    @Override\n    public boolean canApplyAtEnchantingTable(ItemStack stack, Enchantment enchantment) {\n        return enchantment == Enchantments.SILK_TOUCH || super.canApplyAtEnchantingTable(stack, enchantment);\n    }\n\n    private static void setToolMode(ItemStack tool, ExchangingModes mode) {\n        //Store the tool's mode in NBT as a string\n        CompoundTag tagCompound = tool.getOrCreateTag();\n        tagCompound.putString(\"mode\", mode.toString());\n    }\n\n    public static ExchangingModes getToolMode(ItemStack tool) {\n        CompoundTag tagCompound = tool.getOrCreateTag();\n        return ExchangingModes.getFromName(tagCompound.getString(\"mode\"));\n    }\n\n    @Override\n    public void appendHoverText(ItemStack stack, @Nullable Level world, List<Component> tooltip, TooltipFlag flag) {\n        super.appendHoverText(stack, world, tooltip, flag);\n        addEnergyInformation(tooltip, stack);\n\n        ExchangingModes mode = getToolMode(stack);\n        tooltip.add(TooltipTranslation.GADGET_MODE\n                .componentTranslation((mode == ExchangingModes.SURFACE && getConnectedArea(stack)\n                        ? TooltipTranslation.GADGET_CONNECTED.format(Component.translatable(mode.getTranslationKey()).getString())\n                        : Component.translatable(mode.getTranslationKey())))\n                .setStyle(Styles.AQUA));\n\n        tooltip.add(TooltipTranslation.GADGET_BLOCK\n                .componentTranslation(LangUtil.getFormattedBlockName(getToolBlock(stack).getState()))\n                .setStyle(Styles.DK_GREEN));\n\n        int range = getToolRange(stack);\n        tooltip.add(TooltipTranslation.GADGET_RANGE\n                .componentTranslation(range, getRangeInBlocks(range, mode.getMode()))\n                .setStyle(Styles.LT_PURPLE));\n\n        tooltip.add(TooltipTranslation.GADGET_FUZZY\n                .componentTranslation(String.valueOf(getFuzzy(stack)))\n                .setStyle(Styles.GOLD));\n\n        addInformationRayTraceFluid(tooltip, stack);\n    }\n\n    @Override\n    public InteractionResultHolder<ItemStack> use(Level world, Player player, InteractionHand hand) {\n        ItemStack itemstack = player.getItemInHand(hand);\n        player.startUsingItem(hand);\n        if (!world.isClientSide) {\n            if (player.isShiftKeyDown()) {\n                InteractionResultHolder<Block> result = selectBlock(itemstack, player);\n                if (!result.getResult().consumesAction()) {\n                    player.displayClientMessage(MessageTranslation.INVALID_BLOCK.componentTranslation(RegistryUtils.getBlockId(result.getObject())).setStyle(Styles.AQUA), true);\n                    return super.use(world, player, hand);\n                }\n            } else if (player instanceof ServerPlayer) {\n                exchange((ServerPlayer) player, itemstack);\n            }\n        } else {\n            if (!player.isShiftKeyDown()) {\n                BaseRenderer.updateInventoryCache();\n            } else {\n                if (Screen.hasControlDown()) {\n                    PacketHandler.sendToServer(new PacketBindTool());\n                }\n            }\n        }\n        return new InteractionResultHolder<>(InteractionResult.SUCCESS, itemstack);\n    }\n\n    public void setMode(ItemStack heldItem, int modeInt) {\n        //Called when we specify a mode with the radial menu\n        ExchangingModes mode = ExchangingModes.values()[modeInt];\n        setToolMode(heldItem, mode);\n    }\n\n    public static void rangeChange(Player player, ItemStack heldItem) {\n        int range = getToolRange(heldItem);\n        int changeAmount = (getToolMode(heldItem) == ExchangingModes.GRID || (range % 2 == 0)) ? 1 : 2;\n        if (player.isShiftKeyDown()) {\n            range = (range <= 1) ? Config.GADGETS.maxRange.get() : range - changeAmount;\n        } else {\n            range = (range >= Config.GADGETS.maxRange.get()) ? 1 : range + changeAmount;\n        }\n        setToolRange(heldItem, range);\n        player.displayClientMessage(MessageTranslation.RANGE_SET.componentTranslation(range).setStyle(Styles.AQUA), true);\n    }\n\n    private void exchange(ServerPlayer player, ItemStack stack) {\n        ServerLevel world = player.getLevel();\n        ItemStack heldItem = getGadget(player);\n        if (heldItem.isEmpty())\n            return;\n\n        BlockData blockData = getToolBlock(heldItem);\n\n        // Don't attempt to do anything if we can't actually do it.\n        BlockHitResult lookingAt = VectorHelper.getLookingAt(player, stack);\n        BlockEntity tileEntity = world.getBlockEntity(lookingAt.getBlockPos());\n        BlockState lookingAtState = player.level.getBlockState(lookingAt.getBlockPos());\n        Block lookAtBlock = lookingAtState.getBlock();\n        if (blockData.getState() == Blocks.AIR.defaultBlockState()\n                || lookAtBlock == OurBlocks.EFFECT_BLOCK.get()\n                || blockData.getState() == lookingAtState\n                || tileEntity != null) {\n            return;\n        }\n\n        // Get the anchor or build the collection\n        Optional<List<BlockPos>> anchor = GadgetUtils.getAnchor(stack);\n        List<BlockPos> coords = anchor.orElseGet(\n                () -> getToolMode(stack).getMode().getCollection(new AbstractMode.UseContext(world, player, blockData.getState(), lookingAt.getBlockPos(), heldItem, lookingAt.getDirection(), getConnectedArea(heldItem)), player)\n        );\n\n        if (anchor.isPresent()) {\n            setAnchor(stack); // Remove the anchor\n        }\n\n        IItemIndex index = InventoryHelper.index(stack, player);\n\n        //TODO replace fakeWorld\n        fakeWorld.setWorldAndState(player.level, blockData.getState(), coords); // Initialize the fake world's blocks\n        for (BlockPos coordinate : coords) {\n            //Get the extended block state in the fake world Disabled to fix Chisel\n            //state = state.getBlock().getExtendedState(state, fakeWorld, coordinate);\n            exchangeBlock(world, player, index, coordinate, blockData);\n        }\n    }\n\n    private void exchangeBlock(ServerLevel world, ServerPlayer player, IItemIndex index, BlockPos pos, BlockData setBlock) {\n        BlockState currentBlock = world.getBlockState(pos);\n        ITileEntityData data;\n\n        BlockEntity te = world.getBlockEntity(pos);\n        if (te instanceof ConstructionBlockTileEntity) {\n            data = ((ConstructionBlockTileEntity) te).getConstructionBlockData().getTileData();\n            currentBlock = ((ConstructionBlockTileEntity) te).getConstructionBlockData().getState();\n        } else\n            data = TileSupport.createTileData(world, pos);\n\n        ItemStack tool = getGadget(player);\n        if (tool.isEmpty() || !this.canUse(tool, player))\n            return;\n\n        BuildContext buildContext = BuildContext.builder()\n                .stack(tool)\n                .player(player)\n                .build(world);\n\n        MaterialList requiredItems = setBlock.getRequiredItems(buildContext, null, pos);\n        MatchResult match = index.tryMatch(requiredItems);\n        boolean useConstructionPaste = false;\n        if (!match.isSuccess()) {\n            if (setBlock.getState().hasBlockEntity())\n                return;\n            match = index.tryMatch(InventoryHelper.PASTE_LIST);\n            if (!match.isSuccess())\n                return;\n            else\n                useConstructionPaste = true;\n        }\n\n        if (!player.mayBuild() || !world.mayInteract(player, pos))\n            return;\n\n        BlockSnapshot blockSnapshot = BlockSnapshot.create(world.dimension(), world, pos);\n        BlockEvent.BreakEvent e = new BlockEvent.BreakEvent(world, pos, currentBlock, player);\n        if (ForgeEventFactory.onBlockPlace(player, blockSnapshot, Direction.UP) || MinecraftForge.EVENT_BUS.post(e))\n            return;\n\n        this.applyDamage(tool, player);\n\n        if (index.applyMatch(match)) {\n            MaterialList materials = te instanceof ConstructionBlockTileEntity ? InventoryHelper.PASTE_LIST : data.getRequiredItems(\n                    buildContext,\n                    currentBlock,\n                    world.clip(new ClipContext(player.position(), Vec3.atLowerCornerOf(pos), ClipContext.Block.COLLIDER, Fluid.NONE, player)),\n                    pos);\n\n            Iterator<ImmutableMultiset<IUniqueObject<?>>> it = materials.iterator();\n            Multiset<IUniqueObject<?>> producedItems = LinkedHashMultiset.create();\n\n            if (buildContext.getStack().isEnchanted() && EnchantmentHelper.getItemEnchantmentLevel(Enchantments.SILK_TOUCH, buildContext.getStack()) > 0) {\n                producedItems = it.hasNext() ? it.next() : ImmutableMultiset.of();\n            } else {\n                List<ItemStack> drops = Block.getDrops(currentBlock, (ServerLevel) buildContext.getWorld(), pos, buildContext.getWorld().getBlockEntity(pos));\n                producedItems.addAll(drops.stream().map(UniqueItem::ofStack).collect(Collectors.toList()));\n            }\n\n            index.insert(producedItems);\n\n            EffectBlock.spawnEffectBlock(world, pos, setBlock, EffectBlock.Mode.REPLACE, useConstructionPaste);\n        }\n    }\n\n    public static ItemStack getGadget(Player player) {\n        ItemStack stack = AbstractGadget.getGadget(player);\n        if (!(stack.getItem() instanceof GadgetExchanger))\n            return ItemStack.EMPTY;\n\n        return stack;\n    }\n\n    @Override\n    public int getUseDuration(ItemStack stack) {\n        return 20;\n    }\n\n    @Override\n    public boolean performRotate(ItemStack stack, Player player) {\n        GadgetUtils.rotateOrMirrorToolBlock(stack, player, PacketRotateMirror.Operation.ROTATE);\n        return true;\n    }\n\n    @Override\n    public boolean performMirror(ItemStack stack, Player player) {\n        GadgetUtils.rotateOrMirrorToolBlock(stack, player, PacketRotateMirror.Operation.MIRROR);\n        return true;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/items/OurItems.java",
    "content": "package com.direwolf20.buildinggadgets.common.items;\n\nimport com.direwolf20.buildinggadgets.common.blocks.OurBlocks;\nimport com.direwolf20.buildinggadgets.common.config.Config;\nimport com.direwolf20.buildinggadgets.common.util.ref.Reference;\nimport net.minecraft.world.item.BlockItem;\nimport net.minecraft.world.item.Item;\nimport net.minecraftforge.registries.DeferredRegister;\nimport net.minecraftforge.registries.ForgeRegistries;\nimport net.minecraftforge.registries.RegistryObject;\n\npublic final class OurItems {\n    private OurItems() {}\n\n    public static final DeferredRegister<Item> ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, Reference.MODID);\n\n    // Gadgets\n    public static final RegistryObject<Item> BUILDING_GADGET_ITEM = ITEMS.register(\"gadget_building\", GadgetBuilding::new);\n    public static final RegistryObject<Item> EXCHANGING_GADGET_ITEM = ITEMS.register(\"gadget_exchanging\", GadgetExchanger::new);\n    public static final RegistryObject<Item> COPY_PASTE_GADGET_ITEM = ITEMS.register(\"gadget_copy_paste\", GadgetCopyPaste::new);\n    public static final RegistryObject<Item> DESTRUCTION_GADGET_ITEM = ITEMS.register(\"gadget_destruction\", GadgetDestruction::new);\n\n\n    // Construction Paste Containers\n    public static final RegistryObject<Item> PASTE_CONTAINER_T1_ITEM = ITEMS.register(\"construction_paste_container_t1\", () -> new ConstructionPasteContainer(false, Config.PASTE_CONTAINERS.capacityT1::get));\n\n    public static final RegistryObject<Item> PASTE_CONTAINER_T2_ITEM\n            = ITEMS.register(\"construction_paste_container_t2\", () -> new ConstructionPasteContainer(false, Config.PASTE_CONTAINERS.capacityT2::get));\n    public static final RegistryObject<Item> PASTE_CONTAINER_T3_ITEM\n            = ITEMS.register(\"construction_paste_container_t3\", () -> new ConstructionPasteContainer(false, Config.PASTE_CONTAINERS.capacityT3::get));\n    public static final RegistryObject<Item> PASTE_CONTAINER_CREATIVE_ITEM\n            = ITEMS.register(\"construction_paste_container_creative\", () -> new ConstructionPasteContainer(true));\n\n\n    // Construction Paste\n    public static final RegistryObject<Item> CONSTRUCTION_PASTE_ITEM = ITEMS.register(\"construction_paste\", ConstructionPaste::new);\n    public static final RegistryObject<Item> CONSTRUCTION_PASTE_DENSE_ITEM = ITEMS.register(\"construction_chunk_dense\", () -> new Item(itemProperties()));\n\n    // Template\n    public static final RegistryObject<Item> TEMPLATE_ITEM = ITEMS.register(\"template\", TemplateItem::new);\n\n    // Item Blocks\n    public static final RegistryObject<Item> CONSTRUCTION_ITEM\n            = ITEMS.register(\"construction_block\", () -> new BlockItem(OurBlocks.CONSTRUCTION_BLOCK.get(), OurItems.itemProperties()));\n    public static final RegistryObject<Item> CONSTRUCTION_DENSE_ITEM\n            = ITEMS.register(\"construction_block_dense\", () -> new BlockItem(OurBlocks.CONSTRUCTION_DENSE_BLOCK.get(), OurItems.itemProperties()));\n    public static final RegistryObject<Item> CONSTRUCTION_POWDER_ITEM\n            = ITEMS.register(\"construction_block_powder\", () -> new BlockItem(OurBlocks.CONSTRUCTION_POWDER_BLOCK.get(), OurItems.itemProperties()));\n    public static final RegistryObject<Item> TEMPLATE_MANGER_ITEM\n            = ITEMS.register(\"template_manager\", () -> new BlockItem(OurBlocks.TEMPLATE_MANGER_BLOCK.get(), OurItems.itemProperties()));\n\n    public static Item.Properties itemProperties() {\n        return new Item.Properties();\n    }\n\n    public static Item.Properties nonStackableItemProperties() {\n        return itemProperties().stacksTo(1);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/items/TemplateItem.java",
    "content": "package com.direwolf20.buildinggadgets.common.items;\n\nimport com.direwolf20.buildinggadgets.client.screen.GuiMod;\nimport com.direwolf20.buildinggadgets.common.capability.CapabilityTemplate;\nimport com.direwolf20.buildinggadgets.common.capability.provider.TemplateKeyProvider;\nimport com.direwolf20.buildinggadgets.common.util.GadgetUtils;\nimport net.minecraft.world.item.TooltipFlag;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.item.Item;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.world.InteractionResultHolder;\nimport net.minecraft.world.InteractionHand;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.world.level.Level;\nimport net.minecraftforge.common.capabilities.ICapabilityProvider;\n\nimport javax.annotation.Nullable;\nimport java.util.List;\n\npublic class TemplateItem extends Item {\n\n    public TemplateItem() {\n        super(OurItems.itemProperties().stacksTo(1));\n    }\n\n    @Nullable\n    @Override\n    public ICapabilityProvider initCapabilities(ItemStack stack, @Nullable CompoundTag nbt) {\n        return new TemplateKeyProvider(stack);\n    }\n\n    @Override\n    public void appendHoverText(ItemStack stack, @Nullable Level worldIn, List<Component> tooltip, TooltipFlag flagIn) {\n        super.appendHoverText(stack, worldIn, tooltip, flagIn);\n        GadgetUtils.addTooltipNameAndAuthor(stack, worldIn, tooltip);\n    }\n\n    @Override\n    public InteractionResultHolder<ItemStack> use(Level worldIn, Player playerIn, InteractionHand handIn) {\n        if( !playerIn.isShiftKeyDown() )\n            return super.use(worldIn, playerIn, handIn);\n\n        if (worldIn.isClientSide) {\n            return GuiMod.MATERIAL_LIST.openScreen(playerIn)\n                    ? InteractionResultHolder.success(playerIn.getItemInHand(handIn))\n                    : super.use(worldIn, playerIn, handIn);\n        }\n\n        return super.use(worldIn, playerIn, handIn);\n    }\n\n    public static ItemStack getTemplateItem(Player player) {\n        ItemStack mainhand = player.getMainHandItem();\n        if (mainhand.getCapability(CapabilityTemplate.TEMPLATE_KEY_CAPABILITY).isPresent())\n            return mainhand;\n\n        ItemStack offhand = player.getOffhandItem();\n        if (offhand.getCapability(CapabilityTemplate.TEMPLATE_KEY_CAPABILITY).isPresent())\n            return offhand;\n\n        return ItemStack.EMPTY;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/items/modes/AbstractMode.java",
    "content": "package com.direwolf20.buildinggadgets.common.items.modes;\n\nimport com.direwolf20.buildinggadgets.common.blocks.OurBlocks;\nimport com.direwolf20.buildinggadgets.common.config.Config;\nimport com.direwolf20.buildinggadgets.common.items.AbstractGadget;\nimport com.direwolf20.buildinggadgets.common.tileentities.ConstructionBlockTileEntity;\nimport com.direwolf20.buildinggadgets.common.util.GadgetUtils;\nimport com.direwolf20.buildinggadgets.common.util.helpers.VectorHelper;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.world.InteractionHand;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.context.BlockPlaceContext;\nimport net.minecraft.world.item.context.UseOnContext;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.StairBlock;\nimport net.minecraft.world.level.block.entity.BlockEntity;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.level.material.Material;\nimport net.minecraft.world.phys.shapes.Shapes;\n\nimport java.util.Comparator;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\npublic abstract class AbstractMode {\n    private boolean isExchanging;\n\n    public AbstractMode(boolean isExchanging) {\n        this.isExchanging = isExchanging;\n    }\n\n    abstract List<BlockPos> collect(UseContext context, Player player, BlockPos start);\n\n    /**\n     * Gets the collection with filters applied stopping us having to handle the filters in the actual collection\n     * method from having to handle the world etc.\n     */\n    public List<BlockPos> getCollection(UseContext context, Player player) {\n        BlockPos startPos = this.withOffset(context);\n\n        // We don't need this unless we're using the exchanger but I also don't want to\n        // have to remake the state for every block.\n        BlockState lookingAtState = isExchanging() ? context.getWorldState(startPos) : null;\n\n        // We alternate the validator as the exchanger requires a more in-depth validation process.\n        return collect(context, player, startPos)\n                .stream()\n                .filter(e -> isExchanging ? this.exchangingValidator(e, lookingAtState, context) : this.validator(e, context))\n                .sorted(Comparator.comparing((BlockPos pos) -> player.blockPosition().distSqr(pos)))\n                .collect(Collectors.toList());\n    }\n\n    /**\n     * This method does the barest minimum checking that is needed by most modes\n     *\n     * @param context the use context instance\n     * @return if the block is valid\n     */\n    public boolean validator(BlockPos pos, UseContext context) {\n        if (!context.getWorldState(pos).canBeReplaced(context.createBlockUseContext()))\n            return false;\n\n        if (context.world.isOutsideBuildHeight(pos))\n            return false;\n\n        return Config.GENERAL.allowOverwriteBlocks.get()\n                ? context.getWorldState(pos).getMaterial().isReplaceable()\n                : context.getWorldState(pos).getMaterial() != Material.AIR;\n    }\n\n    private boolean exchangingValidator(BlockPos pos, BlockState lookingAtState, UseContext context) {\n        BlockState worldBlockState = context.getWorldState(pos);\n        BlockEntity te = context.getWorld().getBlockEntity(pos);\n\n        // No air! or water\n        if (worldBlockState.getMaterial() == Material.AIR || worldBlockState.getMaterial().isLiquid())\n            return false;\n\n        // No effect blocks and don't try with the same block as you're trying to exchange with\n        if (worldBlockState == OurBlocks.EFFECT_BLOCK.get().defaultBlockState()\n                || worldBlockState == context.getSetState())\n            return false;\n\n        // No tiles unless construction block\n        if (te != null && (!(te instanceof ConstructionBlockTileEntity) || te.getBlockState() == context.getSetState()))\n            return false;\n\n        // Don't exchange bedrock\n        if (worldBlockState.getDestroySpeed(context.getWorld(), pos) < 0)\n            return false;\n\n        if (worldBlockState.getBlock().defaultBlockState() != lookingAtState.getBlock().defaultBlockState() && !context.isFuzzy())\n            return false;\n\n        // Finally, ensure at least a single face is exposed.\n        boolean hasSingeValid = false;\n        for (Direction direction : Direction.values()) {\n            BlockPos offset = pos.relative(direction);\n            BlockState state = context.getWorld().getBlockState(offset);\n            if (state.isAir()\n                    || (state.getShape(context.getWorld(), offset) != Shapes.block() && !(state.getBlock() instanceof StairBlock))) {\n                hasSingeValid = true;\n                break;\n            }\n        }\n\n        return hasSingeValid;\n    }\n\n    public BlockPos withOffset(UseContext context) {\n        return context.placeOnTop ? context.startPos.relative(context.hitSide, 1) : context.startPos;\n    }\n\n    public boolean isExchanging() {\n        return isExchanging;\n    }\n\n    public static class UseContext {\n        private final Level world;\n        private final BlockState setState;\n        private final BlockPos startPos;\n        private final Direction hitSide;\n        private final Player player;\n\n        private final boolean isFuzzy;\n        private final boolean placeOnTop;\n        private final int range;\n        private final boolean rayTraceFluid;\n        private final boolean isConnected;\n\n        public UseContext(Level world, Player player, BlockState setState, BlockPos startPos, ItemStack gadget, Direction hitSide, boolean placeOnTop, boolean isConnected) {\n            this.world = world;\n            this.setState = setState;\n            this.startPos = startPos;\n            this.player = player;\n\n            this.range = GadgetUtils.getToolRange(gadget);\n            this.isFuzzy = AbstractGadget.getFuzzy(gadget);\n            this.rayTraceFluid = AbstractGadget.shouldRayTraceFluid(gadget);\n            this.hitSide = hitSide;\n\n            this.isConnected = isConnected;\n            this.placeOnTop = placeOnTop;\n        }\n\n        public UseContext(Level world, Player player, BlockState setState, BlockPos startPos, ItemStack gadget, Direction hitSide, boolean isConnected) {\n            this(world, player, setState, startPos, gadget, hitSide, false, isConnected);\n        }\n\n        public BlockPlaceContext createBlockUseContext() {\n            return new BlockPlaceContext(\n                    new UseOnContext(\n                            player,\n                            InteractionHand.MAIN_HAND,\n                            VectorHelper.getLookingAt(player, this.rayTraceFluid)\n                    )\n            );\n        }\n\n        public boolean isConnected() {\n            return isConnected;\n        }\n\n        public BlockState getWorldState(BlockPos pos) {\n            return world.getBlockState(pos);\n        }\n\n        public Level getWorld() {\n            return world;\n        }\n\n        public BlockState getSetState() {\n            return setState;\n        }\n\n        public boolean isFuzzy() {\n            return isFuzzy;\n        }\n\n        public boolean isRayTraceFluid() {\n            return rayTraceFluid;\n        }\n\n        public boolean isPlaceOnTop() {\n            return placeOnTop;\n        }\n\n        public int getRange() {\n            return range;\n        }\n\n        public BlockPos getStartPos() {\n            return startPos;\n        }\n\n        public Direction getHitSide() {\n            return this.hitSide;\n        }\n\n        public Player getPlayer() {\n            return player;\n        }\n\n        @Override\n        public String toString() {\n            return \"UseContext{\" +\n                    \"world=\" + world +\n                    \", setState=\" + setState +\n                    \", startPos=\" + startPos +\n                    \", hitSide=\" + hitSide +\n                    \", isFuzzy=\" + isFuzzy +\n                    \", placeOnTop=\" + placeOnTop +\n                    \", range=\" + range +\n                    \", rayTraceFluid=\" + rayTraceFluid +\n                    '}';\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/items/modes/BuildToMeMode.java",
    "content": "package com.direwolf20.buildinggadgets.common.items.modes;\n\nimport com.direwolf20.buildinggadgets.common.config.Config;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.core.BlockPos;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class BuildToMeMode extends AbstractMode {\n    public BuildToMeMode() { super(false); }\n\n    @Override\n    public List<BlockPos> collect(UseContext context, Player player, BlockPos start) {\n        List<BlockPos> coordinates = new ArrayList<>();\n\n        XYZ facingXYZ = XYZ.fromFacing(context.getHitSide());\n\n        int startCoord = XYZ.posToXYZ(start, facingXYZ);\n        int playerCoord = XYZ.posToXYZ(player.blockPosition(), facingXYZ);\n\n        // Clamp the value to the max range of the gadgets raytrace\n        double difference = Math.max(0, Math.min(Config.GENERAL.rayTraceRange.get(), Math.abs(startCoord - playerCoord)));\n        for( int i = 0; i < difference; i ++)\n            coordinates.add(XYZ.extendPosSingle(i, start, context.getHitSide(), facingXYZ));\n\n        return coordinates;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/items/modes/BuildingModes.java",
    "content": "package com.direwolf20.buildinggadgets.common.items.modes;\n\nimport java.util.Arrays;\n\npublic enum BuildingModes {\n    BUILD_TO_ME(new BuildToMeMode(), \"build_to_me\"),\n    VERTICAL_COLUMN(new VerticalColumnMode(false), \"vertical_column\"),\n    HORIZONTAL_COLUMN(new HorizontalColumnMode(false), \"horizontal_column\"),\n    VERTICAL_WALL(new VerticalWallMode(), \"vertical_wall\"),\n    HORIZONTAL_WALL(new HorizontalWallMode(), \"horizontal_wall\"),\n    STAIR(new StairMode(), \"stairs\"),\n    GRID(new GridMode(false), \"grid\"),\n    SURFACE(new SurfaceMode(false), \"surface\");\n\n    AbstractMode mode;\n    String name;\n    BuildingModes(AbstractMode mode, String name) {\n        this.mode = mode;\n        this.name = name;\n    }\n\n    public AbstractMode getMode() {\n        return mode;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public String getTranslationKey() {\n        return \"buildinggadgets.modes.\" + name;\n    }\n\n    public String getIcon() {\n        return \"textures/gui/mode/\" + name + \".png\";\n    }\n\n    public static BuildingModes getFromName(String name) {\n        return Arrays.stream(BuildingModes.values())\n                .filter(e -> e.toString().equals(name))\n                .findFirst()\n                .orElse(BUILD_TO_ME);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/items/modes/ExchangingModes.java",
    "content": "package com.direwolf20.buildinggadgets.common.items.modes;\n\nimport java.util.Arrays;\n\npublic enum ExchangingModes {\n    SURFACE(new SurfaceMode(true), \"surface\"),\n    GRID(new GridMode(true), \"grid\"),\n    VERTICAL_COLUMN(new VerticalColumnMode(true), \"vertical_column\"),\n    HORIZONTAL_COLUMN(new HorizontalColumnMode(true), \"horizontal_column\");\n\n    AbstractMode mode;\n    String name;\n\n    ExchangingModes(AbstractMode mode, String name) {\n        this.mode = mode;\n        this.name = name;\n    }\n\n    public AbstractMode getMode() {\n        return mode;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public String getTranslationKey() {\n        return \"buildinggadgets.modes.\" + name;\n    }\n\n    public String getIcon() {\n        return \"textures/gui/mode/\" + name + \".png\";\n    }\n\n    public static ExchangingModes getFromName(String name) {\n        return Arrays.stream(ExchangingModes.values())\n                .filter(e -> e.toString().equals(name))\n                .findFirst()\n                .orElse(SURFACE);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/items/modes/GridMode.java",
    "content": "package com.direwolf20.buildinggadgets.common.items.modes;\n\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.core.BlockPos;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class GridMode extends AbstractMode {\n    public GridMode(boolean isExchanging) {\n        super(isExchanging);\n    }\n\n    @Override\n    List<BlockPos> collect(UseContext context, Player player, BlockPos start) {\n        List<BlockPos> coordinates = new ArrayList<>();\n\n        // Not sure on why we add 1 to the range but sure?\n        int range = context.getRange() + 1;\n\n        for (int x = range * -7 / 5; x <= range * 7 / 5; x++) {\n            for (int z = range * -7 / 5; z <= range * 7 / 5; z++) {\n                if (x % (((range - 2) % 6) + 2) != 0 || z % (((range - 2) % 6) + 2) != 0)\n                    continue;\n\n                coordinates.add(new BlockPos(start.getX() + x, start.getY(), start.getZ() + z));\n            }\n        }\n\n        return coordinates;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/items/modes/HorizontalColumnMode.java",
    "content": "package com.direwolf20.buildinggadgets.common.items.modes;\n\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.core.Direction;\nimport net.minecraft.core.BlockPos;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class HorizontalColumnMode extends AbstractMode {\n    public HorizontalColumnMode(boolean isExchanging) {\n        super(isExchanging);\n    }\n\n    @Override\n    List<BlockPos> collect(UseContext context, Player player, BlockPos start) {\n        List<BlockPos> coordinates = new ArrayList<>();\n\n        Direction side = XYZ.isAxisY(context.getHitSide()) ? player.getDirection() : context.getHitSide().getOpposite();\n        if( !isExchanging() ) {\n            for (int i = 0; i < context.getRange(); i++)\n                coordinates.add(XYZ.extendPosSingle(i, start, side, XYZ.fromFacing(side)));\n        } else {\n            side = side.getClockWise();\n            int halfRange = context.getRange() / 2;\n            for (int i = -halfRange; i <= halfRange; i++)\n                coordinates.add(XYZ.extendPosSingle(i, start, side, XYZ.fromFacing(side)));\n        }\n\n        return coordinates;\n    }\n}\n\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/items/modes/HorizontalWallMode.java",
    "content": "package com.direwolf20.buildinggadgets.common.items.modes;\n\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.core.BlockPos;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class HorizontalWallMode extends AbstractMode {\n    public HorizontalWallMode() { super(false); }\n\n    @Override\n    List<BlockPos> collect(UseContext context, Player player, BlockPos start) {\n        List<BlockPos> coordinates = new ArrayList<>();\n\n        // Handle top and bottom first.\n        int halfRange = context.getRange() / 2;\n        if( XYZ.isAxisY(context.getHitSide()) ) {\n            for (int i = -halfRange; i <= halfRange; i ++) {\n                for(int j = -halfRange; j <= halfRange; j++)\n                    coordinates.add(new BlockPos(start.getX() - i, start.getY(), start.getZ() + j));\n            }\n\n            return coordinates;\n        }\n\n        // Draw complete column then expand by half the range on both sides :D\n        XYZ xyz = XYZ.fromFacing(context.getHitSide());\n        for (int i = 0; i < context.getRange(); i ++) {\n            for(int j = -halfRange; j <= halfRange; j++) {\n                int value = XYZ.invertOnFace(context.getHitSide(), i);\n                coordinates.add(\n                        xyz == XYZ.X\n                                ? new BlockPos(start.getX() + value, start.getY(), start.getZ() + j)\n                                : new BlockPos(start.getX() + j, start.getY(), start.getZ() + value)\n                );\n            }\n        }\n\n        return coordinates;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/items/modes/StairMode.java",
    "content": "package com.direwolf20.buildinggadgets.common.items.modes;\n\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.core.Direction;\nimport net.minecraft.core.BlockPos;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * This implementation is pretty complicate due the convenience settings for placement. We do a relative simple math\n * equation to start with, stairs are simple, take the look vector, then in a single direction x + 1, then increase the height.\n * <p>\n * It gets complicated though as we need to support the following options\n * - If the player is higher than the target block we should build the stairs in reverse up to the player\n * - If the player is looking at the block one under their feet, and we're on a horizontal facing plane, we should build\n *   outwards away from the facing plain.\n * - If we're looking at the top or bottom of a block, we correct the positioning to build as if we were looking at a face\n *   of a block that wasn't the top or bottom.\n */\npublic class StairMode extends AbstractMode {\n    public StairMode() { super(false); }\n\n    @Override\n    List<BlockPos> collect(UseContext context, Player player, BlockPos start) {\n        List<BlockPos> coordinates = new ArrayList<>();\n\n        Direction side = context.getHitSide();\n        if (XYZ.isAxisY(side))\n            side = player.getDirection().getOpposite();\n\n        for( int i = 0; i < context.getRange(); i ++ ) {\n            var hitSide = context.getHitSide();\n            int shiftAxis = (i + 1) * (hitSide == Direction.EAST || hitSide == Direction.SOUTH ? 1 : -1);\n\n            // Special case for looking at block under your feet and looking at the horizontal axis\n            if (context.getStartPos().getY() < player.getY() && hitSide.getAxis().isHorizontal()) {\n                boolean mutateXAxis = hitSide.getAxis() == Direction.Axis.X;\n                boolean mutateZAxis = hitSide.getAxis() == Direction.Axis.Z;\n\n                coordinates.add(context.getStartPos().offset(\n                        mutateXAxis ? shiftAxis : 0, // If we're hitting at the X axis we should shift it\n                        context.isPlaceOnTop() ? -i : -(i + 1), // If place on top is on, we should place aside the block, otherwise, in the current one\n                        mutateZAxis ? shiftAxis : 0 // If we're hitting at the Z axis we should shift the Z axis of the block.\n                ));\n\n                continue;\n            }\n\n            shiftAxis = i * (side == Direction.EAST || side == Direction.SOUTH ? -1 : 1);\n            if (start.getY() < player.getY() - 2) {\n                shiftAxis = shiftAxis * -1;\n            }\n\n            coordinates.add(start.offset(\n                    side.getAxis() == Direction.Axis.X ? shiftAxis : 0,\n                    start.getY() > (player.getY() + 1) ? i * -1 : i, // Check to see if we should build up or down from the player\n                    side.getAxis() == Direction.Axis.Z ? shiftAxis : 0\n            ));\n        }\n\n        return coordinates;\n    }\n\n    @Override\n    public BlockPos withOffset(UseContext context) {\n        var side = context.getHitSide();\n        var placeOnTop = context.isPlaceOnTop();\n        var pos = context.getStartPos();\n\n        // Is top / bottom? Do as normal. Not? then place on top or inside :D\n        if (side == Direction.DOWN) {\n            return placeOnTop ? pos.relative(side, 1) : pos;\n        }\n\n        return XYZ.isAxisY(side) ? super.withOffset(context) : (placeOnTop ? pos.relative(Direction.UP) : pos);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/items/modes/SurfaceMode.java",
    "content": "package com.direwolf20.buildinggadgets.common.items.modes;\n\nimport com.direwolf20.buildinggadgets.common.tainted.building.Region;\nimport com.direwolf20.buildinggadgets.common.tainted.building.placement.ConnectedSurface;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.core.BlockPos;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\npublic class SurfaceMode extends AbstractMode {\n    public SurfaceMode(boolean isExchanging) { super(isExchanging); }\n\n    @Override\n    List<BlockPos> collect(UseContext context, Player player, BlockPos start) {\n        int bound = context.getRange() / 2;\n\n        // Grow the area. getXOffset will invert some math for us\n        Region area = new Region(start).expand(\n                bound * (1 - Math.abs(context.getHitSide().getStepX())),\n                bound * (1 - Math.abs(context.getHitSide().getStepY())),\n                bound * (1 - Math.abs(context.getHitSide().getStepZ()))\n        );\n\n        if (!context.isConnected()) {\n            return area.stream().map(BlockPos::immutable).collect(Collectors.toList());\n        }\n\n        List<BlockPos> coords = new ArrayList<>();\n\n        ConnectedSurface.create(area, context.getWorld(), pos -> isExchanging() ? pos : pos.relative(context.getHitSide().getOpposite()), start, context.getHitSide().getOpposite(), context.getRange(), context.isFuzzy())\n                .spliterator()\n                .forEachRemaining(coords::add);\n\n        return coords;\n    }\n\n    @Override\n    public boolean validator(BlockPos pos, UseContext context) {\n        // Do our default checks, then do our more complex fuzzy aware checks.\n        boolean topRow = super.validator(pos, context);\n        if( this.isExchanging() )\n            return topRow;\n\n        BlockState startState = context.getWorldState(context.getStartPos());\n        if( context.isFuzzy() )\n            return topRow && !context.getWorld().isEmptyBlock(pos.relative(context.getHitSide().getOpposite()));\n\n        return topRow && context.getWorld().getBlockState(pos.relative(context.getHitSide().getOpposite())) == startState;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/items/modes/VerticalColumnMode.java",
    "content": "package com.direwolf20.buildinggadgets.common.items.modes;\n\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.core.Direction;\nimport net.minecraft.core.BlockPos;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class VerticalColumnMode extends AbstractMode {\n    public VerticalColumnMode(boolean isExchanging) {\n        super(isExchanging);\n    }\n\n    @Override\n    List<BlockPos> collect(UseContext context, Player player, BlockPos start) {\n        List<BlockPos> coordinates = new ArrayList<>();\n\n        // If up or down, full height from start block\n        int halfRange = context.getRange() / 2;\n\n        if( XYZ.isAxisY(context.getHitSide()) ) {\n            // The exchanger handles the Y completely differently :sad: means more code\n            if( isExchanging() ) {\n                Direction playerFacing = player.getDirection();\n                for (int i = -halfRange; i <= halfRange; i++)\n                    coordinates.add(XYZ.extendPosSingle(i, start, playerFacing, XYZ.fromFacing(playerFacing)));\n            } else {\n                for (int i = 0; i < context.getRange(); i++)\n                    coordinates.add(XYZ.extendPosSingle(i, start, context.getHitSide(), XYZ.Y));\n            }\n        // Else, half and half\n        } else {\n            for (int i = -halfRange; i <= halfRange; i++)\n                coordinates.add(XYZ.extendPosSingle(i, start, context.getHitSide(), XYZ.Y));\n        }\n\n        return coordinates;\n    }\n\n    /**\n     * We need to modify where the offset is for this mode as when looking at any\n     * face that isn't up or down, we need to push the offset back into the block\n     * and ignore placeOnTop as this mode does the action by default.\n     */\n    @Override\n    public BlockPos withOffset(UseContext context) {\n        return XYZ.isAxisY(context.getHitSide()) ? super.withOffset(context) : context.getStartPos();\n    }\n}\n\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/items/modes/VerticalWallMode.java",
    "content": "package com.direwolf20.buildinggadgets.common.items.modes;\n\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.phys.AABB;\n\nimport java.util.List;\n\n/**\n * @implNote I'm 100% sure I could solve the xyz == XYZ.X by using a helper method but I'm happy\n *           with it for now.\n */\npublic class VerticalWallMode extends AbstractMode {\n    public VerticalWallMode() { super(false); }\n\n    @Override\n    List<BlockPos> collect(UseContext context, Player player, BlockPos start) {\n        int size = (context.getRange() - 1) / 2;\n\n        Direction hitSide = context.getHitSide();\n        var side = hitSide.getAxis() == Direction.Axis.Y ? player.getDirection().getOpposite().getAxis() : hitSide.getAxis();\n\n        var startY = hitSide.getAxis() == Direction.Axis.Y ? start.getY() : start.getY() - size;\n        var endY = hitSide.getAxis() == Direction.Axis.Y ? start.getY() + ((context.getRange() - 1) * (hitSide == Direction.DOWN ? -1 : 1)) : start.getY() + size;\n\n        AABB box = new AABB(\n            start.getX() - (side == Direction.Axis.Z ? size : 0), startY, start.getZ() - (side == Direction.Axis.X ? size : 0),\n            start.getX() + (side == Direction.Axis.Z ? size : 0), endY, start.getZ() + (side == Direction.Axis.X ? size : 0)\n        );\n\n        return BlockPos.betweenClosedStream(box).map(BlockPos::immutable).toList();\n    }\n\n    /**\n     * We need to modify where the offset is for this mode as when looking at any\n     * face that isn't up or down, we need to push the offset back into the block\n     * and ignore placeOnTop as this mode does the action by default.\n     */\n    @Override\n    public BlockPos withOffset(UseContext context) {\n        return XYZ.isAxisY(context.getHitSide()) ? super.withOffset(context) : context.getStartPos();\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/items/modes/XYZ.java",
    "content": "package com.direwolf20.buildinggadgets.common.items.modes;\n\nimport net.minecraft.core.Direction;\nimport net.minecraft.core.BlockPos;\n\n/**\n * Mostly used to turn a {@link Direction} into an {@link XYZ} representation.\n * Some common methods have been added to actual do computation based on the result\n * of the XYZ.\n *\n * Most of the cleverness to this class is the transform from Direction to an X, Y or Z.\n * I don't have a better name for this atm, soz :P\n */\npublic enum XYZ {\n    X, Y, Z;\n\n    public static XYZ fromFacing(Direction facing) {\n        if( facing == Direction.SOUTH || facing == Direction.NORTH )\n            return XYZ.Z;\n\n        if( facing == Direction.EAST || facing == Direction.WEST )\n            return XYZ.X;\n\n        return XYZ.Y;\n    }\n\n    public static boolean isAxisX(Direction facing) {\n        return facing == Direction.EAST || facing == Direction.WEST;\n    }\n\n    public static boolean isAxisY(Direction facing) {\n        return facing == Direction.UP || facing == Direction.DOWN;\n    }\n\n    public static boolean isAxisZ(Direction facing) {\n        return facing == Direction.SOUTH || facing == Direction.NORTH;\n    }\n\n    public static int posToXYZ(BlockPos pos, XYZ xyz) {\n        if( xyz == X ) return pos.getX();\n        if( xyz == Y ) return pos.getY();\n\n        return pos.getZ();\n    }\n\n    /**\n     * Expands the original block pos by the new offset value. This only happens on a single\n     * dimension.\n     */\n    public static BlockPos extendPosSingle(int value, BlockPos pos, Direction facing, XYZ xyz) {\n        int change = invertOnFace(facing, value);\n\n        if( xyz == X ) return new BlockPos(pos.getX() + change, pos.getY(), pos.getZ());\n        if( xyz == Y ) return new BlockPos(pos.getX(), pos.getY() + change, pos.getZ());\n\n        return new BlockPos(pos.getX(), pos.getY(), pos.getZ() + change);\n    }\n\n    /**\n     * Inverts the value based on the face. If you're facing in a negative direction\n     * the value will be flipped to a negative and vise versa.\n     *\n     * @param facing Looking at face\n     * @param value  Value to be modified\n     * @return modified value\n     */\n    public static int invertOnFace(Direction facing, int value) {\n        return value * ((facing == Direction.NORTH || facing == Direction.DOWN || facing == Direction.WEST) ? -1 : 1);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/network/PacketHandler.java",
    "content": "package com.direwolf20.buildinggadgets.common.network;\n\nimport com.direwolf20.buildinggadgets.common.network.packets.*;\nimport com.direwolf20.buildinggadgets.common.network.split.PacketSplitManager;\nimport com.direwolf20.buildinggadgets.common.network.split.SplitPacket;\nimport com.direwolf20.buildinggadgets.common.util.ref.Reference;\nimport net.minecraft.network.FriendlyByteBuf;\nimport net.minecraft.resources.ResourceLocation;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.minecraftforge.common.util.FakePlayer;\nimport net.minecraftforge.network.NetworkDirection;\nimport net.minecraftforge.network.NetworkEvent;\nimport net.minecraftforge.network.NetworkRegistry;\nimport net.minecraftforge.network.PacketDistributor;\nimport net.minecraftforge.network.simple.SimpleChannel;\n\nimport java.util.function.BiConsumer;\nimport java.util.function.Function;\nimport java.util.function.Supplier;\n\npublic class PacketHandler {\n    private static final String PROTOCOL_VERSION = Integer.toString(4);\n    private static short index = 0;\n    private static final PacketSplitManager SPLIT_MANAGER = new PacketSplitManager();\n\n    public static final SimpleChannel HANDLER = NetworkRegistry.newSimpleChannel(\n            new ResourceLocation(Reference.MODID, \"main\"),\n            () -> PROTOCOL_VERSION,\n            PROTOCOL_VERSION::equals,\n            PROTOCOL_VERSION::equals\n    );\n\n    public static PacketSplitManager getSplitManager() {\n        return SPLIT_MANAGER;\n    }\n\n    public static void register() {\n        // Server side\n        registerMessage(PacketAnchor.class, PacketAnchor::encode, PacketAnchor::decode, PacketAnchor.Handler::handle);\n        registerMessage(PacketBindTool.class, PacketBindTool::encode, PacketBindTool::decode, PacketBindTool.Handler::handle);\n        registerMessage(PacketToggleFuzzy.class, PacketToggleFuzzy::encode, PacketToggleFuzzy::decode, PacketToggleFuzzy.Handler::handle);\n        registerMessage(PacketToggleFluidOnly.class, PacketToggleFluidOnly::encode, PacketToggleFluidOnly::decode, PacketToggleFluidOnly.Handler::handle);\n        registerMessage(PacketToggleConnectedArea.class, PacketToggleConnectedArea::encode, PacketToggleConnectedArea::decode, PacketToggleConnectedArea.Handler::handle);\n        registerMessage(PacketToggleRayTraceFluid.class, PacketToggleRayTraceFluid::encode, PacketToggleRayTraceFluid::decode, PacketToggleRayTraceFluid.Handler::handle);\n        registerMessage(PacketToggleBlockPlacement.class, PacketToggleBlockPlacement::encode, PacketToggleBlockPlacement::decode, PacketToggleBlockPlacement.Handler::handle);\n        registerMessage(PacketChangeRange.class, PacketChangeRange::encode, PacketChangeRange::decode, PacketChangeRange.Handler::handle);\n        registerMessage(PacketRotateMirror.class, PacketRotateMirror::encode, PacketRotateMirror::decode, PacketRotateMirror.Handler::handle);\n        registerMessage(PacketCopyCoords.class, PacketCopyCoords::encode, PacketCopyCoords::decode, PacketCopyCoords.Handler::handle);\n        registerMessage(PacketDestructionGUI.class, PacketDestructionGUI::encode, PacketDestructionGUI::decode, PacketDestructionGUI.Handler::handle);\n        registerMessage(PacketPasteGUI.class, PacketPasteGUI::encode, PacketPasteGUI::decode, PacketPasteGUI.Handler::handle);\n        registerMessage(PacketToggleMode.class, PacketToggleMode::encode, PacketToggleMode::decode, PacketToggleMode.Handler::handle);\n        registerMessage(PacketUndo.class, PacketUndo::encode, PacketUndo::decode, PacketUndo.Handler::handle);\n\n        // Both Sides\n        registerMessage(SplitPacket.class, SPLIT_MANAGER::encode, SPLIT_MANAGER::decode, SPLIT_MANAGER::handle);\n        getSplitManager().registerSplitPacket(SplitPacketUpdateTemplate.class, SplitPacketUpdateTemplate::encode, SplitPacketUpdateTemplate::new, SplitPacketUpdateTemplate::handle);\n\n        registerMessage(PacketSetRemoteInventoryCache.class, PacketSetRemoteInventoryCache::encode, PacketSetRemoteInventoryCache::decode, PacketSetRemoteInventoryCache.Handler::handle);\n        registerMessage(PacketRequestTemplate.class, PacketRequestTemplate::encode, PacketRequestTemplate::new, PacketRequestTemplate::handle);\n\n        // Client side\n        registerMessage(PacketTemplateManagerTemplateCreated.class, PacketTemplateManagerTemplateCreated::encode, PacketTemplateManagerTemplateCreated::new, PacketTemplateManagerTemplateCreated::handle);\n    }\n\n    public static void sendTo(Object msg, ServerPlayer player) {\n        if (!(player instanceof FakePlayer))\n            HANDLER.sendTo(msg, player.connection.connection, NetworkDirection.PLAY_TO_CLIENT);\n    }\n\n    public static void sendToServer(Object msg) {\n        HANDLER.sendToServer(msg);\n    }\n\n    public static void send(Object msg, PacketDistributor.PacketTarget target) {\n        HANDLER.send(target, msg);\n    }\n\n    private static <MSG> void registerMessage(Class<MSG> messageType, BiConsumer<MSG, FriendlyByteBuf> encoder, Function<FriendlyByteBuf, MSG> decoder, BiConsumer<MSG, Supplier<NetworkEvent.Context>> messageConsumer) {\n        HANDLER.registerMessage(index, messageType, encoder, decoder, messageConsumer);\n        index++;\n        if (index > 0xFF)\n            throw new RuntimeException(\"Too many messages!\");\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/network/packets/PacketAnchor.java",
    "content": "package com.direwolf20.buildinggadgets.common.network.packets;\n\nimport com.direwolf20.buildinggadgets.common.items.AbstractGadget;\nimport com.direwolf20.buildinggadgets.common.items.GadgetBuilding;\nimport com.direwolf20.buildinggadgets.common.items.GadgetExchanger;\nimport com.direwolf20.buildinggadgets.common.util.GadgetUtils;\nimport net.minecraft.network.FriendlyByteBuf;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraftforge.network.NetworkEvent;\n\nimport java.util.function.Supplier;\n\npublic class PacketAnchor {\n    public static void encode(PacketAnchor msg, FriendlyByteBuf buffer) {}\n    public static PacketAnchor decode(FriendlyByteBuf buffer) { return new PacketAnchor(); }\n\n    public static class Handler {\n        public static void handle(final PacketAnchor msg, Supplier<NetworkEvent.Context> ctx) {\n            ctx.get().enqueueWork(() -> {\n                Player player = ctx.get().getSender();\n                if (player == null)\n                    return;\n\n                ItemStack stack = AbstractGadget.getGadget(player);\n                if (stack.getItem() instanceof GadgetBuilding)\n                    GadgetUtils.anchorBlocks(player, stack);\n                else if (stack.getItem() instanceof GadgetExchanger)\n                    GadgetUtils.anchorBlocks(player, stack);\n                else\n                    ((AbstractGadget) stack.getItem()).onAnchor(stack, player);\n            });\n            ctx.get().setPacketHandled(true);\n        }\n    }\n}"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/network/packets/PacketBindTool.java",
    "content": "package com.direwolf20.buildinggadgets.common.network.packets;\n\nimport com.direwolf20.buildinggadgets.common.items.AbstractGadget;\nimport com.direwolf20.buildinggadgets.common.items.GadgetDestruction;\nimport com.direwolf20.buildinggadgets.common.util.GadgetUtils;\nimport net.minecraft.network.FriendlyByteBuf;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraftforge.network.NetworkEvent;\n\nimport java.util.function.Supplier;\n\npublic class PacketBindTool {\n    public static void encode(PacketBindTool msg, FriendlyByteBuf buffer) { }\n    public static PacketBindTool decode(FriendlyByteBuf buffer) {\n        return new PacketBindTool();\n    }\n\n    public static class Handler {\n        public static void handle(final PacketBindTool msg, Supplier<NetworkEvent.Context> ctx) {\n            ctx.get().enqueueWork(() -> {\n                Player player = ctx.get().getSender();\n                if (player == null)\n                    return;\n\n                ItemStack stack = AbstractGadget.getGadget(player);\n                if (!(stack.getItem() instanceof GadgetDestruction))\n                    GadgetUtils.linkToInventory(stack, player);\n            });\n            ctx.get().setPacketHandled(true);\n        }\n    }\n}"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/network/packets/PacketChangeRange.java",
    "content": "package com.direwolf20.buildinggadgets.common.network.packets;\n\nimport com.direwolf20.buildinggadgets.common.items.AbstractGadget;\nimport com.direwolf20.buildinggadgets.common.items.GadgetBuilding;\nimport com.direwolf20.buildinggadgets.common.items.GadgetDestruction;\nimport com.direwolf20.buildinggadgets.common.items.GadgetExchanger;\nimport com.direwolf20.buildinggadgets.common.util.GadgetUtils;\nimport net.minecraft.network.FriendlyByteBuf;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraftforge.network.NetworkEvent;\n\nimport java.util.function.Supplier;\n\npublic class PacketChangeRange {\n    private int range;\n\n    public PacketChangeRange() {\n        range = -1;\n    }\n\n    public PacketChangeRange(int range) {\n        this.range = range;\n    }\n\n    public static void encode(PacketChangeRange msg, FriendlyByteBuf buffer) {\n        buffer.writeInt(msg.range);\n    }\n    public static PacketChangeRange decode(FriendlyByteBuf buffer) {\n        return new PacketChangeRange(buffer.readInt());\n    }\n\n    public static class Handler {\n        public static void handle(final PacketChangeRange msg, Supplier<NetworkEvent.Context> ctx) {\n            ctx.get().enqueueWork(() -> {\n                ServerPlayer player = ctx.get().getSender();\n                if (player == null)\n                    return;\n\n                ItemStack stack = AbstractGadget.getGadget(player);\n                if (msg.range >= 0)\n                    GadgetUtils.setToolRange(stack, msg.range);\n                else if (stack.getItem() instanceof GadgetBuilding)\n                    GadgetBuilding.rangeChange(player, stack);\n                else if (stack.getItem() instanceof GadgetExchanger)\n                    GadgetExchanger.rangeChange(player, stack);\n                else if (stack.getItem() instanceof GadgetDestruction)\n                    GadgetDestruction.switchOverlay(player, stack);\n            });\n\n            ctx.get().setPacketHandled(true);\n        }\n    }\n}"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/network/packets/PacketCopyCoords.java",
    "content": "package com.direwolf20.buildinggadgets.common.network.packets;\n\nimport com.direwolf20.buildinggadgets.common.items.GadgetCopyPaste;\nimport com.direwolf20.buildinggadgets.common.tainted.building.Region;\nimport com.direwolf20.buildinggadgets.common.util.lang.MessageTranslation;\nimport com.direwolf20.buildinggadgets.common.util.lang.Styles;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.network.FriendlyByteBuf;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraftforge.network.NetworkEvent;\n\nimport java.util.Optional;\nimport java.util.function.Supplier;\n\npublic class PacketCopyCoords {\n    private final BlockPos start;\n    private final BlockPos end;\n\n    public PacketCopyCoords(BlockPos start, BlockPos end) {\n        this.start = start;\n        this.end = end;\n    }\n\n    public static void encode(PacketCopyCoords msg, FriendlyByteBuf buffer) {\n        buffer.writeBlockPos(msg.start);\n        buffer.writeBlockPos(msg.end);\n    }\n\n    public static PacketCopyCoords decode(FriendlyByteBuf buffer) {\n        return new PacketCopyCoords(buffer.readBlockPos(), buffer.readBlockPos());\n    }\n\n    public static class Handler {\n        public static void handle(final PacketCopyCoords msg, Supplier<NetworkEvent.Context> ctx) {\n            ServerPlayer playerEntity = ctx.get().getSender();\n            if( playerEntity == null ) return;\n\n            ctx.get().enqueueWork(() -> {\n                ItemStack heldItem = GadgetCopyPaste.getGadget(playerEntity);\n                if (heldItem.isEmpty()) return;\n\n                BlockPos startPos = msg.start;\n                BlockPos endPos = msg.end;\n                if (startPos.equals(BlockPos.ZERO) && endPos.equals(BlockPos.ZERO)) {\n                    GadgetCopyPaste.setSelectedRegion(heldItem, null);\n                    playerEntity.displayClientMessage(MessageTranslation.AREA_RESET.componentTranslation().setStyle(Styles.AQUA), true);\n                } else {\n                    GadgetCopyPaste.setSelectedRegion(heldItem, new Region(startPos, endPos));\n                }\n\n                Optional<Region> regionOpt = GadgetCopyPaste.getSelectedRegion(heldItem);\n                if (! regionOpt.isPresent()) //notify of single copy\n                    playerEntity.displayClientMessage(MessageTranslation.FIRST_COPY.componentTranslation().setStyle(Styles.DK_GREEN), true);\n                regionOpt.ifPresent(region -> ((GadgetCopyPaste) heldItem.getItem()).tryCopy(heldItem, playerEntity.level, playerEntity, region));\n            });\n\n            ctx.get().setPacketHandled(true);\n        }\n    }\n}"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/network/packets/PacketDestructionGUI.java",
    "content": "package com.direwolf20.buildinggadgets.common.network.packets;\n\nimport com.direwolf20.buildinggadgets.common.items.GadgetDestruction;\nimport com.direwolf20.buildinggadgets.common.util.ref.NBTKeys;\nimport net.minecraft.network.FriendlyByteBuf;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraftforge.network.NetworkEvent;\n\nimport java.util.function.Supplier;\n\npublic class PacketDestructionGUI {\n\n    private final int left, right, up, down, depth;\n\n    public PacketDestructionGUI(int l, int r, int u, int d, int dep) {\n        left = l;\n        right = r;\n        up = u;\n        down = d;\n        depth = dep;\n    }\n\n    public static void encode(PacketDestructionGUI msg, FriendlyByteBuf buffer) {\n        buffer.writeInt(msg.left);\n        buffer.writeInt(msg.right);\n        buffer.writeInt(msg.up);\n        buffer.writeInt(msg.down);\n        buffer.writeInt(msg.depth);\n    }\n\n    public static PacketDestructionGUI decode(FriendlyByteBuf buffer) {\n        return new PacketDestructionGUI(buffer.readInt(), buffer.readInt(), buffer.readInt(), buffer.readInt(), buffer.readInt());\n    }\n\n    public static class Handler {\n        public static void handle(final PacketDestructionGUI msg, Supplier<NetworkEvent.Context> ctx) {\n            ctx.get().enqueueWork(() -> {\n                ItemStack heldItem = GadgetDestruction.getGadget(ctx.get().getSender());\n                if (heldItem.isEmpty()) return;\n\n                GadgetDestruction.setToolValue(heldItem, msg.left, NBTKeys.GADGET_VALUE_LEFT);\n                GadgetDestruction.setToolValue(heldItem, msg.right, NBTKeys.GADGET_VALUE_RIGHT);\n                GadgetDestruction.setToolValue(heldItem, msg.up, NBTKeys.GADGET_VALUE_UP);\n                GadgetDestruction.setToolValue(heldItem, msg.down, NBTKeys.GADGET_VALUE_DOWN);\n                GadgetDestruction.setToolValue(heldItem, msg.depth, NBTKeys.GADGET_VALUE_DEPTH);\n            });\n\n            ctx.get().setPacketHandled(true);\n        }\n    }\n}"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/network/packets/PacketPasteGUI.java",
    "content": "package com.direwolf20.buildinggadgets.common.network.packets;\n\nimport com.direwolf20.buildinggadgets.common.items.GadgetCopyPaste;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.network.FriendlyByteBuf;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraftforge.network.NetworkEvent;\n\nimport java.util.function.Supplier;\n\npublic class PacketPasteGUI {\n\n    private final int X, Y, Z;\n\n    public PacketPasteGUI(int x, int y, int z) {\n        X = x;\n        Y = y;\n        Z = z;\n    }\n\n    public static void encode(PacketPasteGUI msg, FriendlyByteBuf buffer) {\n        buffer.writeInt(msg.X);\n        buffer.writeInt(msg.Y);\n        buffer.writeInt(msg.Z);\n    }\n\n    public static PacketPasteGUI decode(FriendlyByteBuf buffer) {\n        return new PacketPasteGUI(buffer.readInt(), buffer.readInt(), buffer.readInt());\n    }\n\n    public static class Handler {\n        public static void handle(PacketPasteGUI msg, Supplier<NetworkEvent.Context> ctx) {\n            ctx.get().enqueueWork(() -> {\n                ItemStack heldItem = GadgetCopyPaste.getGadget(ctx.get().getSender());\n                if (heldItem.isEmpty()) return;\n                GadgetCopyPaste.setRelativeVector(heldItem, new BlockPos(msg.X, msg.Y, msg.Z));\n            });\n\n            ctx.get().setPacketHandled(true);\n        }\n    }\n}"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/network/packets/PacketRequestTemplate.java",
    "content": "package com.direwolf20.buildinggadgets.common.network.packets;\n\nimport com.direwolf20.buildinggadgets.client.ClientProxy;\nimport com.direwolf20.buildinggadgets.common.tainted.save.SaveManager;\nimport com.direwolf20.buildinggadgets.common.tainted.template.TemplateKey;\nimport net.minecraft.network.FriendlyByteBuf;\nimport net.minecraftforge.fml.LogicalSide;\nimport net.minecraftforge.network.NetworkEvent;\n\nimport java.util.UUID;\nimport java.util.function.Supplier;\n\npublic final class PacketRequestTemplate extends UUIDPacket {\n    public PacketRequestTemplate(UUID id) {\n        super(id);\n    }\n\n    public PacketRequestTemplate(FriendlyByteBuf buffer) {\n        super(buffer);\n    }\n\n    public void handle(Supplier<NetworkEvent.Context> contextSupplier) {\n        contextSupplier.get().enqueueWork(() -> {\n            if (contextSupplier.get().getDirection().getReceptionSide() == LogicalSide.CLIENT)\n                ClientProxy.CACHE_TEMPLATE_PROVIDER.requestRemoteUpdate(new TemplateKey(getId()));\n            else\n                SaveManager.INSTANCE.getTemplateProvider().requestRemoteUpdate(new TemplateKey(getId()));\n        });\n\n        contextSupplier.get().setPacketHandled(true);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/network/packets/PacketRotateMirror.java",
    "content": "package com.direwolf20.buildinggadgets.common.network.packets;\n\nimport com.direwolf20.buildinggadgets.common.items.AbstractGadget;\nimport net.minecraft.network.FriendlyByteBuf;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraftforge.network.NetworkEvent;\n\nimport javax.annotation.Nullable;\nimport java.util.function.Supplier;\n\npublic class PacketRotateMirror {\n    private Operation operation;\n\n    public PacketRotateMirror() {}\n\n    public PacketRotateMirror(@Nullable Operation operation) {\n        this.operation = operation;\n    }\n\n    public static void encode(PacketRotateMirror msg, FriendlyByteBuf buffer) {\n        boolean hasOperation = msg.operation != null;\n        buffer.writeBoolean(hasOperation);\n        if (hasOperation)\n            buffer.writeInt(msg.operation.ordinal());\n    }\n\n    public static PacketRotateMirror decode(FriendlyByteBuf buffer) {\n        return new PacketRotateMirror(buffer.readBoolean() ? Operation.values()[buffer.readInt()] : null);\n    }\n\n    public enum Operation {\n        ROTATE, MIRROR;\n    }\n\n    public static class Handler {\n        public static void handle(final PacketRotateMirror msg, Supplier<NetworkEvent.Context> ctx) {\n            ctx.get().enqueueWork(() -> {\n                ServerPlayer player = ctx.get().getSender();\n                if (player == null)\n                    return;\n\n                ItemStack stack = AbstractGadget.getGadget(player);\n                Operation operation = msg.operation != null ? msg.operation : (player.isShiftKeyDown() ? Operation.MIRROR : Operation.ROTATE);\n\n                if (operation == Operation.MIRROR) {\n                    ((AbstractGadget) stack.getItem()).onMirror(stack, player);\n                } else {\n                    ((AbstractGadget) stack.getItem()).onRotate(stack, player);\n                }\n            });\n            ctx.get().setPacketHandled(true);\n        }\n    }\n}"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/network/packets/PacketSetRemoteInventoryCache.java",
    "content": "package com.direwolf20.buildinggadgets.common.network.packets;\n\nimport com.direwolf20.buildinggadgets.client.events.EventTooltip;\nimport com.direwolf20.buildinggadgets.client.renders.BaseRenderer;\nimport com.direwolf20.buildinggadgets.common.network.PacketHandler;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.InventoryHelper;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.InventoryLinker;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.materials.objects.UniqueItem;\nimport com.google.common.collect.ImmutableMultiset;\nimport com.google.common.collect.Multiset;\nimport com.google.common.collect.Multiset.Entry;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.registries.Registries;\nimport net.minecraft.network.FriendlyByteBuf;\nimport net.minecraft.resources.ResourceKey;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.minecraft.world.item.Item;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.Level;\nimport net.minecraftforge.network.NetworkEvent;\nimport org.apache.commons.lang3.tuple.ImmutablePair;\nimport org.apache.commons.lang3.tuple.Pair;\n\nimport java.util.HashSet;\nimport java.util.Set;\nimport java.util.function.Supplier;\n\npublic class PacketSetRemoteInventoryCache {\n\n    private final boolean isCopyPaste;\n    private Multiset<UniqueItem> cache;\n    private Pair<BlockPos, ResourceKey<Level>> loc;\n\n    public PacketSetRemoteInventoryCache(Multiset<UniqueItem> cache, boolean isCopyPaste) {\n        this.cache = cache;\n        this.isCopyPaste = isCopyPaste;\n    }\n\n    public PacketSetRemoteInventoryCache(Pair<BlockPos, ResourceKey<Level>> loc, boolean isCopyPaste) {\n        this.loc = loc;\n        this.isCopyPaste = isCopyPaste;\n    }\n\n    public static PacketSetRemoteInventoryCache decode(FriendlyByteBuf buf) {\n        boolean isCopyPaste = buf.readBoolean();\n        if (buf.readBoolean()) {\n            Pair<BlockPos, ResourceKey<Level>> loc = new ImmutablePair<>(\n                    buf.readBlockPos(),\n                    ResourceKey.create(Registries.DIMENSION, buf.readResourceLocation())\n            );\n            return new PacketSetRemoteInventoryCache(loc, isCopyPaste);\n        }\n        int len = buf.readInt();\n        ImmutableMultiset.Builder<UniqueItem> builder = ImmutableMultiset.builder();\n        for (int i = 0; i < len; i++)\n            builder.addCopies(new UniqueItem(Item.byId(buf.readInt())), buf.readInt());\n\n        Multiset<UniqueItem> cache = builder.build();\n        return new PacketSetRemoteInventoryCache(cache, isCopyPaste);\n    }\n\n    public static void encode(PacketSetRemoteInventoryCache msg, FriendlyByteBuf buf) {\n        buf.writeBoolean(msg.isCopyPaste());\n        boolean isRequest = msg.getCache() == null;\n        buf.writeBoolean(isRequest);\n        if (isRequest) {\n            buf.writeLong(msg.getLoc().getLeft().asLong());\n            buf.writeResourceLocation(msg.getLoc().getRight().location());\n            return;\n        }\n        Set<Entry<UniqueItem>> items = msg.getCache().entrySet();\n        buf.writeInt(items.size());\n        for (Entry<UniqueItem> entry : items) {\n            UniqueItem uniqueItem = entry.getElement();\n            buf.writeInt(Item.getId(uniqueItem.createStack().getItem()));\n            buf.writeInt(entry.getCount());\n        }\n    }\n\n    public boolean isCopyPaste() {\n        return isCopyPaste;\n    }\n\n    public Multiset<UniqueItem> getCache() {\n        return cache;\n    }\n\n    public Pair<BlockPos, ResourceKey<Level>> getLoc() {\n        return loc;\n    }\n\n    public static class Handler {\n        public static void handle(final PacketSetRemoteInventoryCache msg, Supplier<NetworkEvent.Context> ctx) {\n            ctx.get().enqueueWork(() -> {\n                ServerPlayer player = ctx.get().getSender();\n                if (player != null) {\n                    Set<UniqueItem> itemTypes = new HashSet<>();\n                    ImmutableMultiset.Builder<UniqueItem> builder = ImmutableMultiset.builder();\n                    InventoryLinker.getLinkedInventory(player.level, msg.loc.getKey(), msg.loc.getValue(), null).ifPresent(inventory -> {\n                        for (int i = 0; i < inventory.getSlots(); i++) {\n                            ItemStack stack = inventory.getStackInSlot(i);\n                            if (!stack.isEmpty()) {\n                                Item item = stack.getItem();\n                                UniqueItem uniqueItem = new UniqueItem(item);\n                                if (!itemTypes.contains(uniqueItem)) {\n                                    itemTypes.add(uniqueItem);\n                                    builder.addCopies(uniqueItem, InventoryHelper.countInContainer(inventory, item));\n                                }\n                            }\n                        }\n                    });\n\n                    PacketHandler.sendTo(new PacketSetRemoteInventoryCache(builder.build(), msg.isCopyPaste()), player);\n                    return;\n                }\n                if (msg.isCopyPaste())\n                    EventTooltip.setCache(msg.getCache());\n                else\n                    BaseRenderer.setInventoryCache(msg.getCache());\n            });\n\n            ctx.get().setPacketHandled(true);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/network/packets/PacketTemplateManagerTemplateCreated.java",
    "content": "package com.direwolf20.buildinggadgets.common.network.packets;\n\nimport com.direwolf20.buildinggadgets.common.BuildingGadgets;\nimport com.direwolf20.buildinggadgets.common.capability.CapabilityTemplate;\nimport com.direwolf20.buildinggadgets.common.items.OurItems;\nimport com.direwolf20.buildinggadgets.common.tainted.template.ITemplateKey;\nimport com.direwolf20.buildinggadgets.common.util.exceptions.CapabilityNotPresentException;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.network.FriendlyByteBuf;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.entity.BlockEntity;\nimport net.minecraftforge.common.capabilities.ForgeCapabilities;\nimport net.minecraftforge.fml.LogicalSide;\nimport net.minecraftforge.items.IItemHandlerModifiable;\nimport net.minecraftforge.network.NetworkEvent;\nimport net.minecraftforge.network.PacketDistributor;\n\nimport java.util.UUID;\nimport java.util.function.Supplier;\n\npublic final class PacketTemplateManagerTemplateCreated extends UUIDPacket {\n    private final BlockPos pos;\n\n    public PacketTemplateManagerTemplateCreated(FriendlyByteBuf buffer) {\n        super(buffer);\n        this.pos = buffer.readBlockPos();\n    }\n\n    public PacketTemplateManagerTemplateCreated(UUID id, BlockPos pos) {\n        super(id);\n        this.pos = pos;\n    }\n\n    @Override\n    public void encode(FriendlyByteBuf buffer) {\n        super.encode(buffer);\n        buffer.writeBlockPos(pos);\n    }\n\n    public void handle(Supplier<NetworkEvent.Context> contextSupplier) {\n        NetworkEvent.Context ctx = contextSupplier.get();\n        ctx.enqueueWork(() -> {\n            if (contextSupplier.get().getDirection().getReceptionSide() == LogicalSide.SERVER) {\n                Level world = contextSupplier.get().getSender().level;\n                if (world.hasChunkAt(pos)) {\n                    BlockEntity tileEntity = world.getBlockEntity(pos);\n                    if (tileEntity != null) {\n                        tileEntity.getCapability(ForgeCapabilities.ITEM_HANDLER).ifPresent(handler -> {\n                            ItemStack stack = new ItemStack(OurItems.TEMPLATE_ITEM.get());\n                            ITemplateKey key = stack.getCapability(CapabilityTemplate.TEMPLATE_KEY_CAPABILITY).orElseThrow(CapabilityNotPresentException::new);\n                            UUID id = key.getTemplateId(this::getId);\n                            if (! id.equals(getId()))\n                                BuildingGadgets.LOG.error(\"Failed to apply Template id on server!\");\n                            else {\n                                ((IItemHandlerModifiable) handler).setStackInSlot(1, stack);\n                                world.getCapability(CapabilityTemplate.TEMPLATE_PROVIDER_CAPABILITY).ifPresent(provider ->\n                                        provider.requestUpdate(key, PacketDistributor.PLAYER.with(() -> contextSupplier.get().getSender())));\n                            }\n                        });\n                    }\n                }\n            }\n        });\n        ctx.setPacketHandled(true);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/network/packets/PacketToggleBlockPlacement.java",
    "content": "package com.direwolf20.buildinggadgets.common.network.packets;\n\nimport com.direwolf20.buildinggadgets.common.items.AbstractGadget;\nimport com.direwolf20.buildinggadgets.common.items.GadgetBuilding;\nimport net.minecraft.network.FriendlyByteBuf;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraftforge.network.NetworkEvent;\n\nimport java.util.function.Supplier;\n\npublic class PacketToggleBlockPlacement {\n\n    public static void encode(PacketToggleBlockPlacement msg, FriendlyByteBuf buffer) {}\n    public static PacketToggleBlockPlacement decode(FriendlyByteBuf buffer) { return new PacketToggleBlockPlacement(); }\n\n    public static class Handler {\n        public static void handle(final PacketToggleBlockPlacement msg, Supplier<NetworkEvent.Context> ctx) {\n            ctx.get().enqueueWork(() -> {\n                ServerPlayer player = ctx.get().getSender();\n                if (player == null)\n                    return;\n\n                ItemStack stack = AbstractGadget.getGadget(player);\n                if (stack.getItem() instanceof GadgetBuilding)\n                    GadgetBuilding.togglePlaceAtop(player, stack);\n            });\n\n            ctx.get().setPacketHandled(true);\n        }\n    }\n}"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/network/packets/PacketToggleConnectedArea.java",
    "content": "package com.direwolf20.buildinggadgets.common.network.packets;\n\nimport com.direwolf20.buildinggadgets.common.items.AbstractGadget;\nimport com.direwolf20.buildinggadgets.common.items.GadgetBuilding;\nimport com.direwolf20.buildinggadgets.common.items.GadgetDestruction;\nimport com.direwolf20.buildinggadgets.common.items.GadgetExchanger;\nimport net.minecraft.network.FriendlyByteBuf;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraftforge.network.NetworkEvent;\n\nimport java.util.function.Supplier;\n\npublic class PacketToggleConnectedArea {\n\n    public static void encode(PacketToggleConnectedArea msg, FriendlyByteBuf buffer) {}\n    public static PacketToggleConnectedArea decode(FriendlyByteBuf buffer) { return new PacketToggleConnectedArea(); }\n\n    public static class Handler {\n        public static void handle(final PacketToggleConnectedArea msg, Supplier<NetworkEvent.Context> ctx) {\n            ctx.get().enqueueWork(() -> {\n                ServerPlayer player = ctx.get().getSender();\n                if (player == null)\n                    return;\n\n                ItemStack stack = AbstractGadget.getGadget(player);\n                if (stack.getItem() instanceof GadgetExchanger || stack.getItem() instanceof GadgetBuilding || stack.getItem() instanceof GadgetDestruction)\n                    AbstractGadget.toggleConnectedArea(player, stack);\n            });\n\n            ctx.get().setPacketHandled(true);\n        }\n    }\n}"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/network/packets/PacketToggleFluidOnly.java",
    "content": "package com.direwolf20.buildinggadgets.common.network.packets;\n\nimport com.direwolf20.buildinggadgets.common.items.AbstractGadget;\nimport com.direwolf20.buildinggadgets.common.items.GadgetDestruction;\nimport net.minecraft.network.FriendlyByteBuf;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraftforge.network.NetworkEvent;\n\nimport java.util.function.Supplier;\n\npublic class PacketToggleFluidOnly {\n    public static void encode(PacketToggleFluidOnly msg, FriendlyByteBuf buffer) {}\n    public static PacketToggleFluidOnly decode(FriendlyByteBuf buffer) { return new PacketToggleFluidOnly(); }\n\n    public static class Handler {\n        public static void handle(final PacketToggleFluidOnly msg, Supplier<NetworkEvent.Context> ctx) {\n            ctx.get().enqueueWork(() -> {\n                ServerPlayer player = ctx.get().getSender();\n                if (player == null)\n                    return;\n\n                ItemStack stack = AbstractGadget.getGadget(player);\n                if (stack.getItem() instanceof GadgetDestruction) {\n                    GadgetDestruction.toggleFluidMode(stack);\n                }\n            });\n            ctx.get().setPacketHandled(true);\n        }\n    }\n}"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/network/packets/PacketToggleFuzzy.java",
    "content": "package com.direwolf20.buildinggadgets.common.network.packets;\n\nimport com.direwolf20.buildinggadgets.common.config.Config;\nimport com.direwolf20.buildinggadgets.common.items.AbstractGadget;\nimport com.direwolf20.buildinggadgets.common.items.GadgetBuilding;\nimport com.direwolf20.buildinggadgets.common.items.GadgetDestruction;\nimport com.direwolf20.buildinggadgets.common.items.GadgetExchanger;\nimport net.minecraft.network.FriendlyByteBuf;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraftforge.network.NetworkEvent;\n\nimport java.util.function.Supplier;\n\npublic class PacketToggleFuzzy {\n\n    public static void encode(PacketToggleFuzzy msg, FriendlyByteBuf buffer) {}\n    public static PacketToggleFuzzy decode(FriendlyByteBuf buffer) { return new PacketToggleFuzzy(); }\n\n    public static class Handler {\n        public static void handle(final PacketToggleFuzzy msg, Supplier<NetworkEvent.Context> ctx) {\n            ctx.get().enqueueWork(() -> {\n                ServerPlayer player = ctx.get().getSender();\n                if (player == null)\n                    return;\n\n                ItemStack stack = AbstractGadget.getGadget(player);\n                if (stack.getItem() instanceof GadgetExchanger || stack.getItem() instanceof GadgetBuilding\n                        || (stack.getItem() instanceof GadgetDestruction && Config.GADGETS.GADGET_DESTRUCTION.nonFuzzyEnabled.get()))\n                    AbstractGadget.toggleFuzzy(player, stack);\n            });\n\n            ctx.get().setPacketHandled(true);\n        }\n    }\n}"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/network/packets/PacketToggleMode.java",
    "content": "package com.direwolf20.buildinggadgets.common.network.packets;\n\nimport com.direwolf20.buildinggadgets.common.items.AbstractGadget;\nimport com.direwolf20.buildinggadgets.common.items.GadgetBuilding;\nimport com.direwolf20.buildinggadgets.common.items.GadgetCopyPaste;\nimport com.direwolf20.buildinggadgets.common.items.GadgetExchanger;\nimport net.minecraft.network.FriendlyByteBuf;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraftforge.network.NetworkEvent;\n\nimport java.util.function.Supplier;\n\npublic class PacketToggleMode {\n    private final int mode;\n\n    public static void encode(PacketToggleMode msg, FriendlyByteBuf buffer) {\n        buffer.writeInt(msg.mode);\n    }\n    public static PacketToggleMode decode(FriendlyByteBuf buffer) {\n        return new PacketToggleMode(buffer.readInt());\n    }\n\n    public PacketToggleMode(int mode) {\n        this.mode = mode;\n    }\n\n    public static class Handler {\n        public static void handle(PacketToggleMode msg, Supplier<NetworkEvent.Context> ctx) {\n            ctx.get().enqueueWork(() -> {\n                ServerPlayer playerEntity = ctx.get().getSender();\n                if( playerEntity == null ) return;\n\n                ItemStack heldItem = AbstractGadget.getGadget(playerEntity);\n                if (heldItem.isEmpty())\n                    return;\n\n                if (heldItem.getItem() instanceof GadgetBuilding) {\n                    GadgetBuilding gadgetBuilding = (GadgetBuilding) (heldItem.getItem());\n                    gadgetBuilding.setMode(heldItem, msg.mode);\n                } else if (heldItem.getItem() instanceof GadgetExchanger) {\n                    GadgetExchanger gadgetExchanger = (GadgetExchanger) (heldItem.getItem());\n                    gadgetExchanger.setMode(heldItem, msg.mode);\n                } else if (heldItem.getItem() instanceof GadgetCopyPaste) {\n                    GadgetCopyPaste gadgetCopyPaste = (GadgetCopyPaste) (heldItem.getItem());\n                    gadgetCopyPaste.setMode(heldItem, msg.mode);\n                }\n            });\n\n            ctx.get().setPacketHandled(true);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/network/packets/PacketToggleRayTraceFluid.java",
    "content": "package com.direwolf20.buildinggadgets.common.network.packets;\n\nimport com.direwolf20.buildinggadgets.common.items.AbstractGadget;\nimport net.minecraft.network.FriendlyByteBuf;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraftforge.network.NetworkEvent;\n\nimport java.util.function.Supplier;\n\npublic class PacketToggleRayTraceFluid {\n\n    public static void encode(PacketToggleRayTraceFluid msg, FriendlyByteBuf buffer) {}\n    public static PacketToggleRayTraceFluid decode(FriendlyByteBuf buffer) { return new PacketToggleRayTraceFluid(); }\n\n    public static class Handler {\n        public static void handle(final PacketToggleRayTraceFluid msg, Supplier<NetworkEvent.Context> ctx) {\n            ctx.get().enqueueWork(() -> {\n                ServerPlayer player = ctx.get().getSender();\n                if (player == null)\n                    return;\n\n                ItemStack stack = AbstractGadget.getGadget(player);\n                if (!stack.isEmpty())\n                    AbstractGadget.toggleRayTraceFluid(player, stack);\n            });\n\n            ctx.get().setPacketHandled(true);\n        }\n    }\n}"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/network/packets/PacketUndo.java",
    "content": "package com.direwolf20.buildinggadgets.common.network.packets;\n\nimport com.direwolf20.buildinggadgets.common.items.AbstractGadget;\nimport com.direwolf20.buildinggadgets.common.items.GadgetExchanger;\nimport net.minecraft.network.FriendlyByteBuf;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraftforge.network.NetworkEvent;\n\nimport java.util.function.Supplier;\n\npublic class PacketUndo {\n\n    public static void encode(PacketUndo msg, FriendlyByteBuf buf) {}\n\n    public static PacketUndo decode(FriendlyByteBuf buf) {\n        return new PacketUndo();\n    }\n\n    public static class Handler {\n        public static void handle(PacketUndo msg, Supplier<NetworkEvent.Context> ctx) {\n            ServerPlayer player = ctx.get().getSender();\n            if (player == null)\n                return;\n\n            ItemStack stack = AbstractGadget.getGadget(player);\n            if (! stack.isEmpty() && !(stack.getItem() instanceof GadgetExchanger))\n                ((AbstractGadget) stack.getItem()).undo(player.level, player, stack);\n\n            ctx.get().setPacketHandled(true);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/network/packets/SplitPacketUpdateTemplate.java",
    "content": "package com.direwolf20.buildinggadgets.common.network.packets;\n\nimport com.direwolf20.buildinggadgets.client.ClientProxy;\nimport com.direwolf20.buildinggadgets.common.tainted.save.SaveManager;\nimport com.direwolf20.buildinggadgets.common.tainted.template.Template;\nimport com.direwolf20.buildinggadgets.common.tainted.template.TemplateIO;\nimport com.direwolf20.buildinggadgets.common.tainted.template.TemplateKey;\nimport com.direwolf20.buildinggadgets.common.util.exceptions.TemplateReadException;\nimport com.direwolf20.buildinggadgets.common.util.exceptions.TemplateWriteException;\nimport net.minecraft.network.FriendlyByteBuf;\nimport net.minecraftforge.fml.LogicalSide;\nimport net.minecraftforge.network.NetworkEvent;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.util.UUID;\nimport java.util.function.Supplier;\n\npublic final class SplitPacketUpdateTemplate extends UUIDPacket {\n    private final Template template;\n\n    public SplitPacketUpdateTemplate(FriendlyByteBuf buffer) {\n        super(buffer);\n        byte[] bytes = new byte[buffer.readableBytes()];\n        buffer.readBytes(bytes);\n        try {\n            template = TemplateIO.readTemplate(new ByteArrayInputStream(bytes), null);\n        } catch (TemplateReadException e) {\n            throw new RuntimeException(\"Failed to read TemplateItem from buffer!\", e);\n        }\n    }\n\n    public SplitPacketUpdateTemplate(UUID id, Template template) {\n        super(id);\n        this.template = template;\n    }\n\n    public void encode(FriendlyByteBuf buffer) {\n        super.encode(buffer);\n        ByteArrayOutputStream stream = new ByteArrayOutputStream();\n        try {\n            TemplateIO.writeTemplate(template, stream);\n            buffer.writeBytes(stream.toByteArray());\n        } catch (TemplateWriteException e) {\n            throw new RuntimeException(\"Failed to write TemplateItem during Packet Encoding!\", e);\n        }\n    }\n\n    public void handle(Supplier<NetworkEvent.Context> contextSupplier) {\n        contextSupplier.get().enqueueWork(() -> {\n            if (contextSupplier.get().getDirection().getReceptionSide() == LogicalSide.CLIENT)\n                ClientProxy.CACHE_TEMPLATE_PROVIDER.setTemplate(new TemplateKey(getId()), template);\n            else\n                SaveManager.INSTANCE.getTemplateProvider().setTemplate(new TemplateKey(getId()), template);\n        });\n\n        contextSupplier.get().setPacketHandled(true);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/network/packets/UUIDPacket.java",
    "content": "package com.direwolf20.buildinggadgets.common.network.packets;\n\nimport net.minecraft.network.FriendlyByteBuf;\n\nimport java.util.UUID;\n\npublic class UUIDPacket {\n    private final UUID id;\n\n    public UUIDPacket(FriendlyByteBuf buffer) {\n        this(buffer.readUUID());\n    }\n\n    public UUIDPacket(UUID id) {\n        this.id = id;\n    }\n\n    public void encode(FriendlyByteBuf buffer) {\n        buffer.writeUUID(id);\n    }\n\n    public UUID getId() {\n        return id;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/network/split/PacketDecoder.java",
    "content": "package com.direwolf20.buildinggadgets.common.network.split;\n\nimport com.direwolf20.buildinggadgets.common.BuildingGadgets;\nimport com.google.common.cache.Cache;\nimport com.google.common.cache.CacheBuilder;\nimport io.netty.buffer.Unpooled;\nimport net.minecraft.network.FriendlyByteBuf;\n\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.ListIterator;\nimport java.util.Optional;\nimport java.util.concurrent.TimeUnit;\nimport java.util.function.Function;\n\nfinal class PacketDecoder<MSG> {\n    private final Cache<Short, PendingPacket> pendingPackets;\n    private final Function<FriendlyByteBuf, MSG> decoder;\n\n    PacketDecoder(Function<FriendlyByteBuf, MSG> decoder) {\n        pendingPackets = CacheBuilder.newBuilder()\n                .expireAfterAccess(1, TimeUnit.MINUTES)\n                .build();\n        this.decoder = decoder;\n    }\n\n    Optional<MSG> decode(SplitPacket packet) {\n        try {\n            return pendingPackets.get(packet.getSessionId(), PendingPacket::new).add(packet);\n        } catch (Exception e) {\n            BuildingGadgets.LOG.error(\"An error occurred whilst assembling packet {} ins session {} with index {} which {}. Discarding.\",\n                    packet.getId(), packet.getSessionId(), packet.getIndex(), packet.hasMore() ? \"has follow up packets\" : \"no follow up packets\", e);\n            pendingPackets.invalidate(packet.getSessionId());\n            return Optional.empty();\n        }\n    }\n\n    private final class PendingPacket {\n        private final List<SplitPacket> partialPackets;\n        private boolean receivedLast;\n\n        private PendingPacket() {\n            partialPackets = new LinkedList<>();\n        }\n\n        private Optional<MSG> add(SplitPacket packet) {\n            if (! packet.hasMore())\n                receivedLast = true;\n            ListIterator<SplitPacket> it = partialPackets.listIterator();\n            while (it.hasNext() && packet != null) {\n                SplitPacket curPacket = it.next();\n                if (packet.getIndex() < curPacket.getIndex()) {\n                    it.set(packet);\n                    it.add(curPacket);\n                    packet = null;\n                }\n            }\n            if (packet != null)\n                partialPackets.add(packet);\n            if (receivedLast)\n                return checkComplete();\n            return Optional.empty();\n        }\n\n        private Optional<MSG> checkComplete() {\n            int index = 0;\n            for (SplitPacket packet : partialPackets) {\n                if (packet.getIndex() != index++)\n                    return Optional.empty();\n            }\n            return assemble();\n        }\n\n        private Optional<MSG> assemble() {\n            FriendlyByteBuf payloadBuffer = new FriendlyByteBuf(Unpooled.buffer(Short.MAX_VALUE, Integer.MAX_VALUE));\n            for (SplitPacket packet : partialPackets) {\n                FriendlyByteBuf payload = packet.getPayload();\n                payloadBuffer.writeBytes(payload);\n            }\n            return Optional.of(decoder.apply(payloadBuffer));\n        }\n\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/network/split/PacketEncoder.java",
    "content": "package com.direwolf20.buildinggadgets.common.network.split;\n\nimport com.google.common.collect.AbstractIterator;\nimport io.netty.buffer.Unpooled;\nimport net.minecraft.network.FriendlyByteBuf;\n\nimport javax.annotation.Nonnull;\nimport java.util.Iterator;\nimport java.util.function.BiConsumer;\n\nimport static com.direwolf20.buildinggadgets.common.network.split.PacketSplitManager.SPLIT_BORDER;\n\nfinal class PacketEncoder<MSG> {\n    private final BiConsumer<MSG, FriendlyByteBuf> messageEncoder;\n    private final int id;\n    private short curSession;\n\n    PacketEncoder(BiConsumer<MSG, FriendlyByteBuf> messageEncoder, int id) {\n        this.messageEncoder = messageEncoder;\n        this.id = id;\n        this.curSession = 0;\n    }\n\n    Iterable<SplitPacket> encode(MSG msg) {\n        return new Iterable<SplitPacket>() { //save some memory by evaluating the copied buffers lazily\n            private short curSession = PacketEncoder.this.curSession++;\n\n            @Override\n            @Nonnull\n            public Iterator<SplitPacket> iterator() {\n                return new AbstractIterator<SplitPacket>() {\n                    private FriendlyByteBuf messageBuffer = new FriendlyByteBuf(Unpooled.buffer(Short.MAX_VALUE, Integer.MAX_VALUE));\n                    private int index = 0;\n\n                    {\n                        messageBuffer.markReaderIndex();\n                        messageBuffer.markWriterIndex();\n                        messageEncoder.accept(msg, messageBuffer);\n                        messageBuffer.resetReaderIndex();\n                    }\n\n                    @Override\n                    protected SplitPacket computeNext() {\n                        if (messageBuffer == null)\n                            return endOfData();\n                        FriendlyByteBuf payload;\n                        boolean hasMore = messageBuffer.readableBytes() > SPLIT_BORDER;\n                        if (hasMore) {\n                            payload = new FriendlyByteBuf(messageBuffer.copy(messageBuffer.readerIndex(), SPLIT_BORDER));\n                            messageBuffer.readerIndex(messageBuffer.readerIndex() + SPLIT_BORDER);\n                        } else {\n                            payload = new FriendlyByteBuf(messageBuffer.copy());\n                            messageBuffer = null; //we are finished - hand it to the GC\n                        }\n                        return new SplitPacket(id, index++, curSession, hasMore, payload);\n                    }\n                };\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/network/split/PacketSplitManager.java",
    "content": "package com.direwolf20.buildinggadgets.common.network.split;\n\nimport com.direwolf20.buildinggadgets.common.network.PacketHandler;\nimport com.google.common.base.Preconditions;\nimport it.unimi.dsi.fastutil.ints.Int2ObjectMap;\nimport it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;\nimport net.minecraft.network.FriendlyByteBuf;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.minecraftforge.network.NetworkEvent;\nimport net.minecraftforge.network.PacketDistributor;\n\nimport java.util.IdentityHashMap;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.function.BiConsumer;\nimport java.util.function.Consumer;\nimport java.util.function.Function;\nimport java.util.function.Supplier;\n\npublic final class PacketSplitManager {\n    public static final int SPLIT_BORDER = 30000;\n    private int id;\n    private final Map<Class<?>, PacketSplitHandler<?>> classToHandlerMap;\n    private final Int2ObjectMap<PacketSplitHandler<?>> idToHandlerMap;\n\n    public PacketSplitManager() {\n        this.classToHandlerMap = new IdentityHashMap<>();\n        this.idToHandlerMap = new Int2ObjectOpenHashMap<>();\n    }\n\n    public <MSG> void registerSplitPacket(Class<MSG> msgClass, BiConsumer<MSG, FriendlyByteBuf> encoder, Function<FriendlyByteBuf, MSG> decoder, BiConsumer<MSG, Supplier<NetworkEvent.Context>> handler) {\n        PacketEncoder<MSG> splitEncoder = new PacketEncoder<>(encoder, id);\n        PacketDecoder<MSG> splitDecoder = new PacketDecoder<>(decoder);\n        PacketSplitHandler<MSG> splitHandler = new PacketSplitHandler<>(splitEncoder, splitDecoder, handler);\n        classToHandlerMap.put(msgClass, splitHandler);\n        idToHandlerMap.put(id++, splitHandler);\n    }\n\n    public void sendTo(Object message, ServerPlayer player) {\n        send(message, packet -> PacketHandler.sendTo(packet, player));\n    }\n\n    public void sendToServer(Object message) {\n        send(message, PacketHandler::sendToServer);\n    }\n\n    public void send(Object message, PacketDistributor.PacketTarget target) {\n        send(message, packet -> PacketHandler.HANDLER.send(target, packet));\n    }\n\n    private void send(Object message, Consumer<SplitPacket> packetConsumer) {\n        @SuppressWarnings(\"unchecked\") //it will only ever have been inserted for the correct class!\n                PacketSplitHandler<Object> handler = (PacketSplitHandler<Object>) classToHandlerMap.get(message.getClass());\n        Preconditions.checkArgument(handler != null, \"Cannot send unknown packet \" + message + \"!\");\n        handler.splitPackets(message, packetConsumer);\n    }\n\n    public void encode(SplitPacket msg, FriendlyByteBuf buf) {\n        msg.writeTo(buf);\n    }\n\n    public SplitPacket decode(FriendlyByteBuf buf) {\n        return SplitPacket.readFrom(buf);\n    }\n\n    public void handle(SplitPacket msg, Supplier<NetworkEvent.Context> ctx) {\n        PacketSplitHandler<?> handler = idToHandlerMap.get(msg.getId());\n        Preconditions.checkArgument(handler != null, \"Cannot handler packet with unknown id \" + msg.getId() + \"!\");\n        handler.handleSplit(msg, ctx);\n    }\n\n    private static final class PacketSplitHandler<MSG> {\n        private final PacketEncoder<MSG> encoder;\n        private final PacketDecoder<MSG> decoder;\n        private final BiConsumer<MSG, Supplier<NetworkEvent.Context>> handler;\n\n        private PacketSplitHandler(PacketEncoder<MSG> encoder, PacketDecoder<MSG> decoder, BiConsumer<MSG, Supplier<NetworkEvent.Context>> handler) {\n            this.encoder = encoder;\n            this.decoder = decoder;\n            this.handler = handler;\n        }\n\n        private void handleSplit(SplitPacket msg, Supplier<NetworkEvent.Context> ctx) {\n            Optional<MSG> msgOpt = decoder.decode(msg);\n            msgOpt.ifPresent(packet -> {\n                handler.accept(packet, ctx);\n                ctx.get().setPacketHandled(true);\n            });\n        }\n\n        private void splitPackets(MSG msg, Consumer<SplitPacket> consumer) {\n            encoder.encode(msg).forEach(consumer);\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/network/split/SplitPacket.java",
    "content": "package com.direwolf20.buildinggadgets.common.network.split;\n\nimport io.netty.buffer.Unpooled;\nimport net.minecraft.network.FriendlyByteBuf;\n\npublic final class SplitPacket {\n    private final int id;\n    private final int index;\n    private final short sessionId;\n    private final boolean hasMore;\n    private final FriendlyByteBuf payload;\n\n    static SplitPacket readFrom(FriendlyByteBuf buffer) {\n        int id = buffer.readVarInt();\n        int index = buffer.readVarInt();\n        short sessionId = buffer.readShort();\n        boolean hasMore = buffer.readBoolean();\n        FriendlyByteBuf payload = new FriendlyByteBuf(Unpooled.buffer(buffer.readableBytes(), Integer.MAX_VALUE));\n        buffer.readBytes(payload);\n        return new SplitPacket(id, index, sessionId, hasMore, payload);\n    }\n\n    SplitPacket(int id, int index, short sessionId, boolean hasMore, FriendlyByteBuf payload) {\n        this.id = id;\n        this.index = index;\n        this.sessionId = sessionId;\n        this.hasMore = hasMore;\n        this.payload = payload;\n    }\n\n    void writeTo(FriendlyByteBuf buffer) {\n        buffer.writeVarInt(id);\n        buffer.writeVarInt(index);\n        buffer.writeShort(sessionId);\n        buffer.writeBoolean(hasMore);\n        buffer.writeBytes(payload);\n    }\n\n    public int getId() {\n        return id;\n    }\n\n    public int getIndex() {\n        return index;\n    }\n\n    public short getSessionId() {\n        return sessionId;\n    }\n\n    public boolean hasMore() {\n        return hasMore;\n    }\n\n    public FriendlyByteBuf getPayload() {\n        return payload;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/Tainted.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * Should be attached to any method, class or var that is inferred to be tainted. This will be used to\n * automatically get a good understanding of how much code needs to be rewritten and what systems are\n * viable to be moved over to the new system\n */\n@Retention(RetentionPolicy.SOURCE)\npublic @interface Tainted {\n    String reason();\n    String resolve() default \"Replace with a new system\";\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/building/BlockData.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.building;\n\nimport com.direwolf20.buildinggadgets.common.BuildingGadgets;\nimport com.direwolf20.buildinggadgets.common.tainted.building.tilesupport.ITileDataSerializer;\nimport com.direwolf20.buildinggadgets.common.tainted.building.tilesupport.ITileEntityData;\nimport com.direwolf20.buildinggadgets.common.tainted.building.tilesupport.TileSupport;\nimport com.direwolf20.buildinggadgets.common.tainted.building.view.BuildContext;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.materials.MaterialList;\nimport com.direwolf20.buildinggadgets.common.tainted.registry.Registries;\nimport com.direwolf20.buildinggadgets.common.util.ref.NBTKeys;\nimport com.direwolf20.buildinggadgets.common.util.tools.RegistryUtils;\nimport com.google.common.base.MoreObjects;\nimport com.google.common.base.Preconditions;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.registries.BuiltInRegistries;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.nbt.NbtUtils;\nimport net.minecraft.world.level.block.Blocks;\nimport net.minecraft.world.level.block.Mirror;\nimport net.minecraft.world.level.block.Rotation;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.phys.HitResult;\n\nimport javax.annotation.Nullable;\nimport java.util.Objects;\nimport java.util.function.IntFunction;\nimport java.util.function.ToIntFunction;\n\n/**\n * Representation of the data one block can hold, in the form of an {@link BlockState} and an instance of {@link ITileEntityData}.\n * This calls offers serialisation facilities as well as delegating placement through to the {@link ITileEntityData}.\n * <p>\n * Notice that this class is immutable as long as the {@link ITileEntityData} instance is immutable.\n */\npublic final class BlockData {\n    public static final BlockData AIR = new BlockData(Blocks.AIR.defaultBlockState(), TileSupport.dummyTileEntityData());\n\n    /**\n     * Attempts to retrieve a BlockData from the given {@link CompoundTag}, if present.\n     *\n     * @param tag       The {@link CompoundTag} representing the serialized block data.\n     * @param persisted Whether or not the {@link CompoundTag} was created using an persisted save.\n     * @return A new instance of {@code BlockData} as represented by the {@link CompoundTag}, if it could be created or null otherwise.\n     * @see #deserialize(CompoundTag, boolean)\n     */\n    @Nullable\n    public static BlockData tryDeserialize(@Nullable CompoundTag tag, boolean persisted) {\n        return tryDeserialize(tag, persisted ? null : i -> RegistryUtils.getById(Registries.TILE_DATA_SERIALIZER_REGISTRY.get(), i), persisted);\n    }\n\n    @Nullable\n    public static BlockData tryDeserialize(@Nullable CompoundTag tag, @Nullable IntFunction<ITileDataSerializer> serializerProvider, boolean readDataPersisted) {\n        if (tag == null || !(tag.contains(NBTKeys.KEY_STATE) && tag.contains(NBTKeys.KEY_SERIALIZER) && tag.contains(NBTKeys.KEY_DATA)))\n            return null;\n\n        // NOTE: Ask around and see if there is a recommended replacement for this `BuiltInRegistries.BLOCK.asLookup()` can't be correct\n        BlockState state = NbtUtils.readBlockState(BuiltInRegistries.BLOCK.asLookup(), tag.getCompound(NBTKeys.KEY_STATE));\n        ITileDataSerializer serializer;\n        try {\n            if (serializerProvider == null)\n                serializer = RegistryUtils\n                        .getFromString(Registries.TILE_DATA_SERIALIZER_REGISTRY.get(), tag.getString(NBTKeys.KEY_SERIALIZER));\n            else\n                serializer = serializerProvider.apply(tag.getInt(NBTKeys.KEY_SERIALIZER));\n        } catch (Exception e) {\n            BuildingGadgets.LOG.error(\"Failed to create deserializer!\", e);\n            return null;\n        }\n        if (serializer == null)\n            return null;\n        ITileEntityData data = serializer.deserialize(tag.getCompound(NBTKeys.KEY_DATA), readDataPersisted);\n        return new BlockData(state, data);\n    }\n\n    /**\n     * @param tag       The {@link CompoundTag} representing the serialized block data.\n     * @param persisted Whether or not the {@link CompoundTag} was created using an persisted save.\n     * @return A new instance of {@code BlockData} as represented by the {@link CompoundTag}.\n     * @throws IllegalArgumentException if the given tag does not represent a valid {@code BlockData}.\n     * @throws NullPointerException     if the tag was null.\n     */\n    public static BlockData deserialize(CompoundTag tag, boolean persisted) {\n        return deserialize(tag, persisted ? null : i -> RegistryUtils.getById(Registries.TILE_DATA_SERIALIZER_REGISTRY.get(), i), persisted);\n    }\n\n    public static BlockData deserialize(CompoundTag tag, @Nullable IntFunction<ITileDataSerializer> serializerProvider, boolean readDataPersisted) {\n        Preconditions.checkNotNull(tag, \"Cannot deserialize from a null tag compound\");\n        Preconditions.checkArgument(tag.contains(NBTKeys.KEY_STATE) && tag.contains(NBTKeys.KEY_SERIALIZER) && tag.contains(NBTKeys.KEY_DATA),\n                \"Given NBTTagCompound does not contain a valid BlockData instance. Missing NBT-Keys in Tag {}!\", tag.toString());\n\n        // NOTE: Ask around and see if there is a recommended replacement for this `BuiltInRegistries.BLOCK.asLookup()` can't be correct\n        BlockState state = NbtUtils.readBlockState(BuiltInRegistries.BLOCK.asLookup(), tag.getCompound(NBTKeys.KEY_STATE));\n        ITileDataSerializer serializer;\n        try {\n            if (serializerProvider == null)\n                serializer = RegistryUtils\n                        .getFromString(Registries.TILE_DATA_SERIALIZER_REGISTRY.get(), tag.getString(NBTKeys.KEY_SERIALIZER));\n            else\n                serializer = serializerProvider.apply(tag.getInt(NBTKeys.KEY_SERIALIZER));\n        } catch (Exception e) {\n            throw new IllegalArgumentException(\"Could not retrieve serializer with persisted=\" + readDataPersisted + \"!\", e);\n        }\n        Preconditions.checkArgument(serializer != null,\n                \"Failed to retrieve serializer for tag {} and persisted={}\", tag.toString(), readDataPersisted);\n        ITileEntityData data = serializer.deserialize(tag.getCompound(NBTKeys.KEY_DATA), readDataPersisted);\n        return new BlockData(state, data);\n    }\n\n    private final BlockState state;\n    private final ITileEntityData tileData;\n\n    /**\n     * Creates a new {@code BlockData} with the specified data.\n     *\n     * @param state    The {@link BlockState} of the resulting data.\n     * @param tileData The {@link ITileEntityData} of the resulting data.\n     * @throws NullPointerException if state or tileData are null.\n     */\n    public BlockData(BlockState state, ITileEntityData tileData) {\n        this.state = Objects.requireNonNull(state);\n        this.tileData = Objects.requireNonNull(tileData);\n    }\n\n    /**\n     * @return The {@link BlockState} contained by this {@code BlockData}\n     */\n    public BlockState getState() {\n        return state;\n    }\n\n    /**\n     * @return The {@link ITileEntityData} contained by this {@code BlockState}.\n     */\n    public ITileEntityData getTileData() {\n        return tileData;\n    }\n\n    /**\n     * @param context The {@link BuildContext} in which to perform the placement.\n     * @param pos     The {@link BlockPos} at which to perform the placement.\n     * @return whether or not the {@link ITileEntityData} reported that placement was performed.\n     */\n    public boolean placeIn(BuildContext context, BlockPos pos) {\n        return tileData.placeIn(context, state, pos);\n    }\n\n    /**\n     * Serializes this {@code BlockData} to NBT. If persisted is false, registry id's will be used instead of registry-names, for serialisation.\n     *\n     * @param persisted Whether or not this should be written as a persisted save.\n     * @return The serialized form of this {@code BlockData}.\n     */\n    public CompoundTag serialize(boolean persisted) {\n        return serialize(persisted ? null : ser -> RegistryUtils.getId(Registries.TILE_DATA_SERIALIZER_REGISTRY.get(), ser), persisted);\n    }\n\n    public CompoundTag serialize(@Nullable ToIntFunction<ITileDataSerializer> idGetter, boolean writeDataPersisted) {\n        CompoundTag tag = new CompoundTag();\n        tag.put(NBTKeys.KEY_STATE, NbtUtils.writeBlockState(state));\n        if (idGetter == null)\n            tag.putString(NBTKeys.KEY_SERIALIZER, Registries.TILE_DATA_SERIALIZER_REGISTRY.get().getKey(tileData.getSerializer()).toString());\n        else\n            tag.putInt(NBTKeys.KEY_SERIALIZER, idGetter.applyAsInt(tileData.getSerializer()));\n        tag.put(NBTKeys.KEY_DATA, tileData.getSerializer().serialize(tileData, writeDataPersisted));\n        return tag;\n    }\n\n    public BlockData mirror(Mirror mirror) {\n        return new BlockData(getState().mirror(mirror), getTileData());\n    }\n\n    public BlockData rotate(Rotation rotation) {\n        return new BlockData(getState().rotate(rotation), getTileData());\n    }\n\n    public MaterialList getRequiredItems(BuildContext context, @Nullable HitResult target, @Nullable BlockPos pos) {\n        return getTileData().getRequiredItems(context, getState(), target, pos);\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) return true;\n        if (!(o instanceof BlockData)) return false;\n\n        BlockData blockData = (BlockData) o;\n\n        if (!getState().equals(blockData.getState())) return false;\n        return getTileData().equals(blockData.getTileData());\n    }\n\n    @Override\n    public int hashCode() {\n        int result = getState().hashCode();\n        result = 31 * result + getTileData().hashCode();\n        return result;\n    }\n\n    @Override\n    public String toString() {\n        return MoreObjects.toStringHelper(this)\n                .add(\"state\", state)\n                .add(\"tileData\", tileData)\n                .toString();\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/building/PlacementChecker.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.building;\n\nimport com.direwolf20.buildinggadgets.common.capability.IPrivateEnergy;\nimport com.direwolf20.buildinggadgets.common.tainted.building.tilesupport.TileSupport;\nimport com.direwolf20.buildinggadgets.common.tainted.building.view.BuildContext;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.IItemIndex;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.InventoryHelper;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.MatchResult;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.materials.MaterialList;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.materials.objects.IUniqueObject;\nimport com.direwolf20.buildinggadgets.common.util.CommonUtils;\nimport com.direwolf20.buildinggadgets.common.util.exceptions.CapabilityNotPresentException;\nimport com.google.common.collect.ImmutableMultiset;\nimport com.google.common.collect.Multiset;\nimport net.minecraft.core.Direction;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.phys.HitResult;\nimport net.minecraftforge.common.MinecraftForge;\nimport net.minecraftforge.common.util.BlockSnapshot;\nimport net.minecraftforge.common.util.LazyOptional;\nimport net.minecraftforge.energy.IEnergyStorage;\nimport net.minecraftforge.event.ForgeEventFactory;\nimport net.minecraftforge.event.level.BlockEvent;\n\nimport java.util.function.BiPredicate;\nimport java.util.function.ToIntFunction;\n\n/**\n * This class performs all Placement checks required for the Copy-Paste-Gadget. Aka it tests for availability of energy, items and free placement-space.\n * You can extract information about whether the tests succeed, paste was used etc. from the CheckResult.\n */\npublic final class PlacementChecker {\n    private final LazyOptional<IEnergyStorage> energyCap;\n    private final ToIntFunction<PlacementTarget> energyFun;\n    private final IItemIndex index;\n    private final boolean firePlaceEvents;\n    private final BiPredicate<BuildContext, PlacementTarget> placeCheck;\n\n    public PlacementChecker(LazyOptional<IEnergyStorage> energyCap, ToIntFunction<PlacementTarget> energyFun, IItemIndex index, BiPredicate<BuildContext, PlacementTarget> placeCheck, boolean firePlaceEvents) {\n        this.energyCap = energyCap;\n        this.energyFun = energyFun;\n        this.index = index;\n        this.firePlaceEvents = firePlaceEvents;\n        this.placeCheck = placeCheck;\n    }\n\n    /**\n     * @implNote This code is so god damn messy. Good luck understanding it.\n     */\n    public CheckResult checkPositionWithResult(BuildContext context, PlacementTarget target, boolean giveBackItems) {\n        if (target.getPos().getY() > context.getWorld().getMaxBuildHeight() || target.getPos().getY() < context.getWorld().getMinBuildHeight() || !placeCheck.test(context, target))\n            return new CheckResult(MatchResult.failure(), ImmutableMultiset.of(), false, false);\n        int energy = energyFun.applyAsInt(target);\n        Multiset<IUniqueObject<?>> insertedItems = ImmutableMultiset.of();\n        boolean isCreative = context.getPlayer() != null && context.getPlayer().isCreative();\n\n        // We're using the IPrivateEnergy interface to get around the simulated power class\n        IPrivateEnergy storage = (IPrivateEnergy) energyCap.orElseThrow(CapabilityNotPresentException::new);\n        if (!isCreative && storage.extractPower(energy, true) != energy)\n            return new CheckResult(MatchResult.failure(), insertedItems, false, false);\n\n        HitResult targetRayTrace = null;\n        if (context.getPlayer() != null) {\n            Player player = context.getPlayer();\n            targetRayTrace = CommonUtils.fakeRayTrace(player.position(), target.getPos());\n        }\n        MaterialList materials = target.getRequiredMaterials(context, targetRayTrace);\n        MatchResult match = index.tryMatch(materials);\n        boolean usePaste = false;\n        if (!match.isSuccess()) {\n            match = index.tryMatch(InventoryHelper.PASTE_LIST);\n            if (!match.isSuccess())\n                return new CheckResult(match, insertedItems, false, false);\n            usePaste = true;\n        }\n\n        BlockSnapshot blockSnapshot = BlockSnapshot.create(context.getServerWorld().dimension(), context.getWorld(), target.getPos());\n        boolean isAir = blockSnapshot.getCurrentBlock().isAir();\n        if (firePlaceEvents && ForgeEventFactory.onBlockPlace(context.getPlayer(), blockSnapshot, Direction.UP))\n            return new CheckResult(match, insertedItems, false, usePaste);\n        if (!isAir) {\n            if (firePlaceEvents) {\n                BlockEvent.BreakEvent e = new BlockEvent.BreakEvent(context.getServerWorld(),\n                        target.getPos(), blockSnapshot.getCurrentBlock(),\n                        context.getPlayer());\n                if (MinecraftForge.EVENT_BUS.post(e))\n                    return new CheckResult(match, insertedItems, false, usePaste);\n            }\n            if (giveBackItems) {\n                insertedItems = TileSupport.createTileData(context.getWorld().getBlockEntity(target.getPos()))\n                        .getRequiredItems(context, blockSnapshot.getCurrentBlock(), null, target.getPos()).iterator().next();\n                index.insert(insertedItems);\n            }\n        }\n        boolean success = true;\n        if (!isCreative)\n            success = storage.extractPower(energy, false) == energy;\n        success = success && index.applyMatch(match);\n        return new CheckResult(match, insertedItems, success, usePaste);\n    }\n\n    public static final class CheckResult {\n        private final MatchResult match;\n        private final Multiset<IUniqueObject<?>> insertedItems;\n        private final boolean success;\n        private final boolean usingPaste;\n\n        private CheckResult(MatchResult match, Multiset<IUniqueObject<?>> insertedItems, boolean success, boolean usingPaste) {\n            this.match = match;\n            this.insertedItems = insertedItems;\n            this.success = success;\n            this.usingPaste = usingPaste;\n        }\n\n        public Multiset<IUniqueObject<?>> getInsertedItems() {\n            return insertedItems;\n        }\n\n        public MatchResult getMatch() {\n            return match;\n        }\n\n        public boolean isSuccess() {\n            return success;\n        }\n\n        public boolean isUsingPaste() {\n            return usingPaste;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/building/PlacementTarget.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.building;\n\nimport com.direwolf20.buildinggadgets.common.tainted.building.tilesupport.ITileEntityData;\nimport com.direwolf20.buildinggadgets.common.tainted.building.view.BuildContext;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.materials.MaterialList;\nimport com.direwolf20.buildinggadgets.common.util.ref.NBTKeys;\nimport com.google.common.base.MoreObjects;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.nbt.NbtUtils;\nimport net.minecraft.world.level.block.Mirror;\nimport net.minecraft.world.level.block.Rotation;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.world.phys.HitResult;\n\nimport javax.annotation.Nonnull;\nimport javax.annotation.Nullable;\nimport java.util.Objects;\n\n/**\n * {@link BlockPos} and {@link BlockData} combined, to allow for placement of an {@link BlockData} in an {@link BuildContext}. Ths class also offers serialisation\n * capabilities, though in general mapping positions to data across a structure wide-map should be preferred, as BlockData:position is a 1:n relationship.\n * <p>\n * Notice that this class is immutable as long as the {@link ITileEntityData} instance of the contained {@link BlockData} is immutable.\n */\npublic final class PlacementTarget {\n\n    /**\n     * @param nbt The {@link CompoundNBT} representing the serialized form of this {@code PlacementTarget}.\n     * @param persisted Flag indicating whether the data was created for an persisted save or not\n     * @return A new {@code PlacementTarget} representing the serialized form of nbt.\n     * @throws IllegalArgumentException if the persisted flag does not match the way the nbt was created\n     * @throws NullPointerException if the serializer for the {@link ITileEntityData} could not be found\n     * @see BlockData#deserialize(CompoundNBT, boolean)\n     */\n    public static PlacementTarget deserialize(CompoundTag nbt, boolean persisted) {\n        BlockPos pos = NbtUtils.readBlockPos(nbt.getCompound(NBTKeys.KEY_POS));\n        BlockData data = BlockData.deserialize(nbt.getCompound(NBTKeys.KEY_DATA), persisted);\n        return new PlacementTarget(pos, data);\n    }\n\n    @Nonnull\n    private final BlockPos pos;\n    @Nonnull\n    private final BlockData data;\n\n    /**\n     * Creates a new {@code PlacementTarget} for the specified position and data\n     * @param pos The {@link BlockPos} at which the data should be placed\n     * @param data The {@link BlockData} to be placed\n     * @throws NullPointerException if position or data are null\n     */\n    public PlacementTarget(@Nonnull BlockPos pos, @Nonnull BlockData data) {\n        this.pos = Objects.requireNonNull(pos);\n        this.data = Objects.requireNonNull(data);\n    }\n\n    /**\n     * @return The {@link BlockPos} at which something should be placed\n     */\n    public BlockPos getPos() {\n        return pos;\n    }\n\n    /**\n     * @return The {@link BlockData} to be placed by this {@code PlacementTarget}.\n     */\n    public BlockData getData() {\n        return data;\n    }\n\n    /**\n     * Attempts to place the {@link BlockData} at the specified position.\n     * @param context The {@link BuildContext} to place in.\n     * @return Whether or not placement was performed by the underlying {@link BlockData}\n     * @see BlockData#placeIn(BuildContext, BlockPos)\n     */\n    public boolean placeIn(BuildContext context) {\n        return data.placeIn(context, pos);\n    }\n\n    public PlacementTarget mirror(Mirror mirror) {\n        return new PlacementTarget(pos, data.mirror(mirror));\n    }\n\n    public PlacementTarget rotate(Rotation rotation) {\n        return new PlacementTarget(getPos(), getData().rotate(rotation));\n    }\n\n    public MaterialList getRequiredMaterials(BuildContext context, @Nullable HitResult target) {\n        return getData().getRequiredItems(context, target, getPos());\n    }\n\n    /**\n     * Serializes the data contained by this {@link PlacementTarget}. The persisted flag is used as an hint, whether non-persistent formats (Registry-id's) may be used to\n     * reduce the size of the resulting {@link CompoundNBT}.\n     * @param persisted Whether or not this should be created as an persisted save.\n     * @return The serialized form of this {@code PlacementTarget} as an {@link CompoundNBT}.\n     * @see BlockData#serialize(boolean)\n     */\n    public CompoundTag serialize(boolean persisted) {\n        CompoundTag compound = new CompoundTag();\n        compound.put(NBTKeys.KEY_DATA, data.serialize(persisted));\n        compound.put(NBTKeys.KEY_POS, NbtUtils.writeBlockPos(pos));\n        return compound;\n    }\n\n    @Override\n    public String toString() {\n        return MoreObjects.toStringHelper(this)\n                .add(\"pos\", pos)\n                .add(\"data\", data)\n                .toString();\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) return true;\n        if (! (o instanceof PlacementTarget)) return false;\n\n        PlacementTarget that = (PlacementTarget) o;\n\n        if (! getPos().equals(that.getPos())) return false;\n        return getData().equals(that.getData());\n    }\n\n    @Override\n    public int hashCode() {\n        int result = getPos().hashCode();\n        result = 31 * result + getData().hashCode();\n        return result;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/building/Region.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.building;\n\nimport com.direwolf20.buildinggadgets.common.util.ref.NBTKeys;\nimport com.google.common.base.MoreObjects;\nimport com.google.common.collect.ImmutableSortedSet;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.world.level.ChunkPos;\nimport net.minecraft.core.Vec3i;\nimport net.minecraft.world.level.LevelReader;\n\nimport java.io.Serializable;\nimport java.util.Comparator;\nimport java.util.Objects;\nimport java.util.stream.Stream;\n\n/**\n * Represents a region in the world with a finite and nonzero size.\n */\npublic final class Region implements Serializable {\n    private static final Region ZERO = new Region(BlockPos.ZERO);\n\n    public static Region singleZero() {\n        return ZERO;\n    }\n\n    /**\n     * Creates a new {@link Builder} which initially contains {@code (0, 0, 0) to (0, 0, 0)}.\n     *\n     * @return A new {@link Builder}\n     */\n    public static Builder builder() {\n        return new Builder();\n    }\n\n    /**\n     * Creates a new {@link Builder} which initially only contains {@code (Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE) to (Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE)}.\n     * <br>\n     * Useful for creating a builder which encloses all Positions passed in.\n     *\n     * @return A new {@link Builder}\n     */\n    public static Builder enclosingBuilder() {\n        return enclosingBuilder(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE);\n    }\n\n    public static Builder enclosingBuilder(int minX, int minY, int minZ, int maxX, int maxY, int maxZ) {\n        return new Builder(minX, minY, minZ, maxX, maxY, maxZ);\n    }\n\n    private static final long serialVersionUID = 8391481277782374853L;\n\n    public static Region deserializeFrom(CompoundTag tag) {\n        return new Region(\n                tag.getInt(NBTKeys.KEY_MIN_X),\n                tag.getInt(NBTKeys.KEY_MIN_Y),\n                tag.getInt(NBTKeys.KEY_MIN_Z),\n                tag.getInt(NBTKeys.KEY_MAX_X),\n                tag.getInt(NBTKeys.KEY_MAX_Y),\n                tag.getInt(NBTKeys.KEY_MAX_Z)\n        );\n    }\n\n    private final int minX;\n    private final int minY;\n    private final int minZ;\n    private final int maxX;\n    private final int maxY;\n    private final int maxZ;\n\n    public Region(int minX, int minY, int minZ, int maxX, int maxY, int maxZ) {\n        this.minX = Math.min(minX, maxX);\n        this.minY = Math.min(minY, maxY);\n        this.minZ = Math.min(minZ, maxZ);\n        this.maxX = Math.max(minX, maxX);\n        this.maxY = Math.max(minY, maxY);\n        this.maxZ = Math.max(minZ, maxZ);\n    }\n\n    public Region(Vec3i vertex) {\n        this(vertex, vertex);\n    }\n\n    public Region(Vec3i min, Vec3i max) {\n        this(min.getX(), min.getY(), min.getZ(), max.getX(), max.getY(), max.getZ());\n    }\n\n    /**\n     * Translates this Region by the given amount\n     *\n     * @param x how much to translate x\n     * @param y how much to translate y\n     * @param z how much to translate z\n     * @return A new Region, translated by the given coordinates\n     */\n    public Region translate(int x, int y, int z) {\n        return new Region(minX + x, minY + y, minZ + z, maxX + x, maxY + y, maxZ + z);\n    }\n\n    /**\n     * @see #translate(int, int, int)\n     *\n     * @param direction Direction to translate to\n     * @return {@link Region}\n     */\n    public Region translate(Vec3i direction) {\n        return this.translate(direction.getX(), direction.getY(), direction.getZ());\n    }\n\n    public Region inverseTranslate(Vec3i direction) {\n        return this.translate(- direction.getX(), - direction.getY(), - direction.getZ());\n    }\n\n    /**\n     * @param x x-growth\n     * @param y y-growth\n     * @param z z-growth\n     * @return A new Region who's max coordinates are increased by the given amount\n     */\n    public Region grow(int x, int y, int z) {\n        return new Region(minX, minY, minZ, maxX + x, maxY + y, maxZ + z);\n    }\n\n    /**\n     * See {@link #grow(int, int, int)} - grown on all three axes.\n     *\n     * @param size Size to grow to\n     * @return {@link Region}\n     */\n    public Region grow(int size) {\n        return this.grow(size, size, size);\n    }\n\n    /**\n     * See {@link #grow(int, int, int)} - subtracting instead of adding.\n     *\n     * @param x X\n     * @param y Y\n     * @param z Z\n     *\n     * @return {@link Region}\n     */\n    public Region shrink(int x, int y, int z) {\n        return this.grow(-x, -y, -z);\n    }\n\n    /**\n     * See {@link #grow(int)} - subtracting instead of adding.\n     * @param size Size to shrink to\n     * @return {@link Region}\n     */\n    public Region shrink(int size) {\n        return this.grow(-size);\n    }\n\n    /**\n     * Expand the current region by the given amount in both directions. Negative\n     * values will shrink the region instead of expanding it.\n     * <p>\n     * Side lengths will be increased by 2 times the value of the parameters, since both min and max are changed.\n     * <p>\n     * If contracting and the amount to contract by is larger than the length of a side, then the side will wrap (still\n     * creating a valid region - see last sample).\n     *\n     * <h3>Samples:</h3>\n     * <table summary=\"A few examples of Regions\">\n     * <tr><th>Input</th><th>Result</th></tr>\n     * <tr><td><pre>{new Region(0, 0, 0, 1, 1, 1).grow(2, 2, 2)}</pre></td><td><pre><code>{box[-2, -2, -2 to 3, 3, 3]}</code></pre></td></tr>\n     * <tr><td><pre>{new Region(0, 0, 0, 6, 6, 6).grow(-2, -2, -2)}</pre></td><td><pre><code>{box[2, 2, 2 to 4, 4, 4]}</code></pre></td></tr>\n     * <tr><td><pre>{new Region(5, 5, 5, 7, 7, 7).grow(0, 1, -1)}</pre></td><td><pre><code>{box[5, 4, 6 to 7, 8, 6]}</code></pre></td></tr>\n     * <tr><td><pre>{new Region(1, 1, 1, 3, 3, 3).grow(-4, -2, -3)}</pre></td><td><pre><code>{box[-1, 1, 0 to 5, 3, 4]}</code></pre></td></tr>\n     * </table>\n     *\n     * <h3>See Also:</h3>\n     * <ul>\n     * <li>{@link #grow(int)} - version of this that expands in all directions from one parameter.\n     * <li>{@link #shrink(int)} - contracts in all directions\n     * </ul>\n     *\n     *\n     * @param x X\n     * @param y Y\n     * @param z Z\n     * @return {@link Region}\n     */\n    public Region expand(int x, int y, int z) {\n        return new Region(minX - x, minY - y, minZ - z, maxX + x, maxY + y, maxZ + z);\n    }\n\n    public Region expand(Vec3i vec) {\n        return expand(vec.getX(), vec.getY(), vec.getZ());\n    }\n\n    /**\n     * Expand the current region by the given value in the max values. Equivalent to   with the given value for all 3 params. Negative values will shrink the region.\n     * <p>\n     * Side lengths will be increased by 2 times the value of the parameter, since both min and max are changed.\n     * <p>\n     * If contracting and the amount to contract by is larger than the length of a side, then the side will wrap (still\n     * creating a valid region - see samples on {@link #grow(int, int, int)}).\n     *\n     *\n     * @param size Size to expand to\n     * @return {@link Region}\n     */\n    public Region expand(int size) {\n        return expand(size, size, size);\n    }\n\n    /**\n     * @param x X\n     * @param y Y\n     * @param z Z\n     * @see #expand(int, int, int) - substracting instead of adding.\n     * @return {@link Region}\n     */\n    public Region collapse(int x, int y, int z) {\n        return expand(-x, -y, -z);\n    }\n\n    /**\n     * @see #collapse(int, int, int) - read x, y, and z from the {@link Vector3i}.\n     * @param vec Vector3i to collapse to\n     * @return {@link Region}\n     */\n    public Region collapse(Vec3i vec) {\n        return collapse(vec.getX(), vec.getY(), vec.getZ());\n    }\n\n    /**\n     * @see #collapse(int, int, int) - collapse on all three axes.\n     *\n     * @param size Size to collapse to\n     * @return {@link Region}\n     */\n    public Region collapse(int size) {\n        return expand(-size);\n    }\n\n    /**\n     * Create a new region with the intersecting part between the two regions.\n     *\n     * @param other another region\n     * @return a new region\n     */\n    public Region intersect(Region other) {\n        int minX = Math.max(this.minX, other.minX);\n        int minY = Math.max(this.minY, other.minY);\n        int minZ = Math.max(this.minZ, other.minZ);\n        int maxX = Math.min(this.maxX, other.maxX);\n        int maxY = Math.min(this.maxY, other.maxY);\n        int maxZ = Math.min(this.maxZ, other.maxZ);\n        return new Region(minX, minY, minZ, maxX, maxY, maxZ);\n    }\n\n    /**\n     * Create a new region that encloses both regions\n     *\n     * @param other another region\n     * @return a new region\n     */\n    public Region union(Region other) {\n        int minX = Math.min(this.minX, other.minX);\n        int minY = Math.min(this.minY, other.minY);\n        int minZ = Math.min(this.minZ, other.minZ);\n        int maxX = Math.max(this.maxX, other.maxX);\n        int maxY = Math.max(this.maxY, other.maxY);\n        int maxZ = Math.max(this.maxZ, other.maxZ);\n        return new Region(minX, minY, minZ, maxX, maxY, maxZ);\n    }\n\n    public int getMinX() {\n        return minX;\n    }\n\n    public int getMinY() {\n        return minY;\n    }\n\n    public int getMinZ() {\n        return minZ;\n    }\n\n    public int getMaxX() {\n        return maxX;\n    }\n\n    public int getMaxY() {\n        return maxY;\n    }\n\n    public int getMaxZ() {\n        return maxZ;\n    }\n\n    public BlockPos getMin() {\n        return new BlockPos(minX, minY, minZ);\n    }\n\n    public BlockPos getMax() {\n        return new BlockPos(maxX, maxY, maxZ);\n    }\n\n    public int getXSize() {\n        return Math.abs(maxX - minX) + 1;\n    }\n\n    public int getYSize() {\n        return Math.abs(maxY - minY) + 1;\n    }\n\n    public int getZSize() {\n        return Math.abs(maxZ - minZ) + 1;\n    }\n\n    public int size() {\n        return getXSize() * getYSize() * getZSize();\n    }\n\n    public boolean containsX(int x) {\n        return x >= minX && x <= maxX;\n    }\n\n    public boolean containsY(int y) {\n        return y >= minY && y <= maxY;\n    }\n\n    public boolean containsZ(int z) {\n        return z >= minZ && z <= maxZ;\n    }\n\n//    /**\n//     * Accurate representation of whether the position is a part the structure or not.\n//     *\n//     * @see #contains(int, int, int)\n//     */\n//    @Override\n    public boolean mayContain(int x, int y, int z) {\n        return contains(x, y, z);\n    }\n\n    /**\n     * @param x X\n     * @param y Y\n     * @param z Z\n     *\n     * @return whether or not this {@link BlockPos} lies within this Region\n     */\n    public boolean contains(int x, int y, int z) {\n        return containsX(x) && containsY(y) && containsZ(z);\n    }\n\n    public boolean contains(Vec3i vec) {\n        return mayContain(vec.getX(), vec.getY(), vec.getZ());\n    }\n\n    public boolean intersectsWith(Region other) {\n        return this.maxX >= other.minX &&\n                this.minX <= other.maxX &&\n                this.maxZ >= other.minZ &&\n                this.minZ <= other.maxZ &&\n                this.maxY >= other.minY &&\n                this.minY <= other.maxY;\n    }\n\n    public Stream<BlockPos> stream() {\n        return BlockPos.betweenClosedStream(minX, minY, minZ, maxX, maxY, maxZ).map(BlockPos::immutable);\n    }\n\n//    /**\n//     * The first result will have the minimum x, y, and z value. In the process it will advance in positive z-y-x order as used in BG-Code on various other places.\n//     * Positions provided by this Iterator may be considered ordered.\n//     *\n//     * @return A {@link PeekingIterator} over all positions in this Region\n//     * @implSpec starts at (minX, minY, minZ), ends at (maxX, maxY, maxZ)\n//     * @implNote the Iterator does not support removal Operations\n//     * @see com.direwolf20.buildinggadgets.common.util.CommonUtils#POSITION_COMPARATOR\n//     */\n//    @Override\n//    public PeekingIterator<BlockPos> iterator() {\n//        return new SpliteratorBackedPeekingIterator<>(spliterator());\n//    }\n\n//    /**\n//     * Creates a {@link Spliterator} over the positions described by this {@code Region}.\n//     *\n//     * @return a {@link Spliterator} over the positions described by this {@code Region}.\n//     * @implSpec The returned {@link Spliterator} will be Immutable, Sorted and Sized\n//     */\n//    @Override\n//    public Spliterator<BlockPos> spliterator() {\n//        return new RegionSpliterator(this);\n//    }\n\n    @Override\n    public String toString() {\n        return MoreObjects.toStringHelper(this)\n                .add(\"minX\", minX)\n                .add(\"minY\", minY)\n                .add(\"minZ\", minZ)\n                .add(\"maxX\", maxX)\n                .add(\"maxY\", maxY)\n                .add(\"maxZ\", maxZ)\n                .toString();\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) return true;\n        if (o == null || getClass() != o.getClass()) return false;\n        Region region = (Region) o;\n        return minX == region.minX &&\n                minY == region.minY &&\n                minZ == region.minZ &&\n                maxX == region.maxX &&\n                maxY == region.maxY &&\n                maxZ == region.maxZ;\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(minX, minY, minZ, maxX, maxY, maxZ);\n    }\n\n    @SuppressWarnings(\"deprecation\")\n    public ImmutableSortedSet<ChunkPos> getUnloadedChunks(LevelReader reader) {\n        ImmutableSortedSet.Builder<ChunkPos> posBuilder = ImmutableSortedSet.orderedBy(Comparator.comparing(ChunkPos::getMinBlockX).thenComparing(ChunkPos::getMinBlockZ));\n        for (int i = minX; i <= maxX; i += 16) {\n            for (int j = minZ; j <= maxZ; j += 16) {\n                if (! reader.hasChunk(i >> 4, j >> 4))\n                    posBuilder.add(new ChunkPos(i >> 4, j >> 4));\n            }\n        }\n        for (int j = minZ; j <= maxZ; j += 16) {//check the last x row\n            if (! reader.hasChunk(maxX >> 4, j >> 4))\n                posBuilder.add(new ChunkPos(maxX >> 4, j >> 4));\n        }\n        if (! reader.hasChunk(maxX >> 4, maxZ >> 4))// might have still missed the last one\n            posBuilder.add(new ChunkPos(maxX >> 4, maxZ >> 4));\n        return posBuilder.build();\n    }\n\n    public CompoundTag serialize() {\n        return serializeTo(new CompoundTag());\n    }\n\n    public CompoundTag serializeTo(CompoundTag tag) {\n        tag.putInt(NBTKeys.KEY_MIN_X, minX);\n        tag.putInt(NBTKeys.KEY_MIN_Y, minY);\n        tag.putInt(NBTKeys.KEY_MIN_Z, minZ);\n        tag.putInt(NBTKeys.KEY_MAX_X, maxX);\n        tag.putInt(NBTKeys.KEY_MAX_Y, maxY);\n        tag.putInt(NBTKeys.KEY_MAX_Z, maxZ);\n        return tag;\n    }\n\n    public static class Builder {\n        private int minX;\n        private int minY;\n        private int minZ;\n        private int maxX;\n        private int maxY;\n        private int maxZ;\n\n        private Builder() {\n            this(0, 0, 0, 0, 0, 0);\n        }\n\n        public Builder(int minX, int minY, int minZ, int maxX, int maxY, int maxZ) {\n            this.minX = minX;\n            this.minY = minY;\n            this.minZ = minZ;\n            this.maxX = maxX;\n            this.maxY = maxY;\n            this.maxZ = maxZ;\n        }\n\n        /**\n         * Allows enclosing of all Vectors in the passed iterable.\n         *\n         * @param iterable The iterable who's contents shall be in the resulting Region\n         * @return The {@code Builder} to allow for Method chaining\n         * @see #enclose(Vector3i)\n         */\n        public Builder encloseAll(Iterable<? extends Vec3i> iterable) {\n            for (Vec3i vec : iterable) {\n                enclose(vec);\n            }\n            return this;\n        }\n\n        public Builder enclose(Region region) {\n            enclose(region.getMin());\n            enclose(region.getMax());\n            return this;\n        }\n\n        /**\n         * @param vec Vex3i\n         * @see #enclose(int, int, int)\n         *\n         * @return {@link Builder}\n         */\n        public Builder enclose(Vec3i vec) {\n            return enclose(vec.getX(), vec.getY(), vec.getZ());\n        }\n\n        /**\n         * Ensures that the passed in coordinates are included in the resulting {@link Region}.\n         *\n         * @param x the x Coordinate which has to be included in the resulting {@link Region}\n         * @param y the y Coordinate which has to be included in the resulting {@link Region}\n         * @param z the z Coordinate which has to be included in the resulting {@link Region}\n         * @return The {@code Builder} to allow for Method chaining\n         * @see #encloseX(int)\n         * @see #encloseY(int)\n         * @see #encloseZ(int)\n         */\n        public Builder enclose(int x, int y, int z) {\n            encloseX(x);\n            encloseY(y);\n            encloseZ(z);\n            return this;\n        }\n\n        /**\n         * Ensures that the passed in x Coordinate is included in the resulting {@link Region}.\n         *\n         * @param x the x Coordinate which has to be included in the resulting {@link Region}\n         * @return The {@code Builder} to allow for Method chaining\n         */\n        public Builder encloseX(int x) {\n            minX = Math.min(x, minX);\n            maxX = Math.max(x, maxX);\n            return this;\n        }\n\n        /**\n         * Ensures that the passed in y Coordinate is included in the resulting {@link Region}.\n         *\n         * @param y the y Coordinate which has to be included in the resulting {@link Region}\n         * @return The {@code Builder} to allow for Method chaining\n         */\n        public Builder encloseY(int y) {\n            minY = Math.min(y, minY);\n            maxY = Math.max(y, maxY);\n            return this;\n        }\n\n        /**\n         * Ensures that the passed in y Coordinate is included in the resulting {@link Region}.\n         *\n         * @param z the z Coordinate which has to be included in the resulting {@link Region}\n         * @return The {@code Builder} to allow for Method chaining\n         */\n        public Builder encloseZ(int z) {\n            minZ = Math.min(z, minZ);\n            maxZ = Math.max(z, maxZ);\n            return this;\n        }\n\n        public Region build() {\n            return new Region(minX, minY, minZ, maxX, maxY, maxZ);\n        }\n    }\n}"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/building/package-info.java",
    "content": "@ParametersAreNonnullByDefault\n@MethodsReturnNonnullByDefault\npackage com.direwolf20.buildinggadgets.common.tainted.building;\n\nimport net.minecraft.MethodsReturnNonnullByDefault;\n\nimport javax.annotation.ParametersAreNonnullByDefault;"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/building/placement/ConnectedSurface.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.building.placement;\n\nimport com.direwolf20.buildinggadgets.common.tainted.building.Region;\nimport com.google.common.collect.AbstractIterator;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.core.Direction;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.world.level.BlockGetter;\n\nimport javax.annotation.Nonnull;\nimport javax.annotation.Nullable;\nimport java.util.*;\nimport java.util.function.BiPredicate;\nimport java.util.function.Function;\n\npublic class ConnectedSurface implements Iterable<BlockPos> {\n    public static ConnectedSurface create(Region searchingRegion, BlockGetter world, BlockPos searchingCenter, Direction side, int range, boolean fuzzy) {\n        return create(searchingRegion, world, pos -> pos.relative(side), searchingCenter, side, range, fuzzy);\n    }\n\n    public static ConnectedSurface create(Region searchingRegion, BlockGetter world, Function<BlockPos, BlockPos> searching2referenceMapper, BlockPos searchingCenter, Direction side, int range, boolean fuzzy) {\n        return create(world, searchingRegion, searching2referenceMapper, searchingCenter, side, fuzzy);\n    }\n\n    public static ConnectedSurface create(BlockGetter world, Region searchingRegion, Function<BlockPos, BlockPos> searching2referenceMapper, BlockPos searchingCenter, @Nullable Direction side, boolean fuzzy) {\n        return new ConnectedSurface(world, searchingRegion, searching2referenceMapper, searchingCenter, side, fuzzy);\n    }\n\n    public static ConnectedSurface create(BlockGetter world, Region searchingRegion, Function<BlockPos, BlockPos> searching2referenceMapper, BlockPos searchingCenter, @Nullable Direction side, BiPredicate<BlockState, BlockPos> predicate) {\n        return new ConnectedSurface(world, searchingRegion, searching2referenceMapper, searchingCenter, side, predicate);\n    }\n\n    private final BlockGetter world;\n    private final Region searchingRegion;\n    private final Function<BlockPos, BlockPos> searching2referenceMapper;\n    private final BlockPos searchingCenter;\n\n    @Nullable\n    private final Direction side;\n    private final BiPredicate<BlockState, BlockPos> predicate;\n\n    ConnectedSurface(BlockGetter world, Region searchingRegion, Function<BlockPos, BlockPos> searching2referenceMapper, BlockPos searchingCenter, @Nullable Direction side, boolean fuzzy) {\n        this(world, searchingRegion, searching2referenceMapper, searchingCenter, side,\n                (filter, pos) -> {\n                    BlockState reference = world.getBlockState(searching2referenceMapper.apply(pos));\n                    boolean isAir = reference.isAir();\n                    // If fuzzy=true, we ignore the block for reference\n                    return ! isAir && (fuzzy || filter == reference);\n                });\n    }\n\n    ConnectedSurface(BlockGetter world, Region searchingRegion, Function<BlockPos, BlockPos> searching2referenceMapper, BlockPos searchingCenter, @Nullable Direction side, BiPredicate<BlockState, BlockPos> predicate) {\n        this.world = world;\n        this.searchingRegion = searchingRegion;\n        this.searching2referenceMapper = searching2referenceMapper;\n        this.searchingCenter = searchingCenter;\n        this.side = side;\n        this.predicate = predicate;\n    }\n\n    /**\n     * The bounding box of the searchingRegion that is being searched.\n     */\n    public Region getBoundingBox() {\n        return searchingRegion;\n    }\n\n    /**\n     * Uses a 8-way adjacent flood fill algorithm with Breadth-First Search to identify blocks with a valid path. A position\n     * is valid if and only if it connects to the center and its underside block is the same as the underside of the center.\n     *\n     * @implNote Uses a 8-way adjacent flood fill algorithm with Breadth-First Search to identify blocks with a valid path.\n     */\n    @Nonnull\n    @Override\n    public Iterator<BlockPos> iterator() {\n        BlockState selectedBlock = getReferenceFor(searchingCenter);\n\n        return new AbstractIterator<BlockPos>() {\n            private Queue<BlockPos> queue = new LinkedList<>();\n            private Set<BlockPos> searched = new HashSet<>(searchingRegion.size());\n\n            {\n                if (isValid(searchingCenter)) { //The destruction Gadget might be facing Bedrock or something similar - this would not be valid!\n                    queue.add(searchingCenter);\n                    searched.add(searchingCenter);\n                }\n            }\n\n            @Override\n            protected BlockPos computeNext() {\n                if (queue.isEmpty())\n                    return endOfData();\n\n                // The position is guaranteed to be valid\n                BlockPos current = queue.remove();\n\n                for (int i = - 1; i <= 1; i++) {\n                    for (int j = - 1; j <= 1; j++) {\n                        if (side != null) {\n                            BlockPos neighbor = perpendicularSurfaceOffset(current, side.getAxis(), i, j);\n                            addNeighbour(neighbor);\n                        } else {\n                            for (int k = - 1; k <= 1; k++) {\n                                BlockPos neighbor = current.offset(i, j, k);\n                                addNeighbour(neighbor);\n                            }\n                        }\n                    }\n                }\n                return current;\n            }\n\n            private void addNeighbour(BlockPos neighbor) {\n                boolean isSearched = ! searched.add(neighbor);\n                if (isSearched || ! isValid(neighbor))\n                    return;\n                queue.add(neighbor);\n            }\n\n            private boolean isValid(BlockPos pos) {\n                return searchingRegion.contains(pos) && predicate.test(selectedBlock, pos);\n            }\n        };\n    }\n\n    private BlockState getReferenceFor(BlockPos pos) {\n        return world.getBlockState(searching2referenceMapper.apply(pos));\n    }\n\n    public BlockPos perpendicularSurfaceOffset(BlockPos pos, Direction.Axis intersector, int i, int j) {\n        switch (intersector) {\n            case X:\n                return pos.offset(0, i, j);\n            case Y:\n                return pos.offset(i, 0, j);\n            case Z:\n                return pos.offset(i, j, 0);\n        }\n        throw new IllegalArgumentException(\"Unknown facing \" + intersector);\n    }\n}\n\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/building/tilesupport/ITileDataFactory.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.building.tilesupport;\n\nimport com.direwolf20.buildinggadgets.common.tainted.registry.Registries.TileEntityData;\nimport net.minecraft.world.level.block.entity.BlockEntity;\n\nimport javax.annotation.Nullable;\n\n/**\n * Function creating {@link ITileEntityData} from a given {@link TileEntity}.\n * <p>\n * The implementations registered to {@link TileEntityData#getTileDataFactories()} (via {@link net.minecraftforge.fml.InterModComms.IMCMessage}'s) will be sorted\n * according to the specified topological boundaries. When queried, they will be called successively until the first implementation returns a\n * non-null value. Therefore overriding an existing {@code ITileDataFactory} is as easy as requiring it to be run after your own implementation.\n * <p>\n * {@link TileEntity TileEntities} wishing for custom {@link ITileEntityData} implementations should instead of registering an additional\n * {@code ITileDataFactory} implement {@link ITileDataProvider}. This is only intended for providing {@link ITileEntityData} implementations\n * for {@link TileEntity TileEntities} who are beyond your own control.\n */\n@FunctionalInterface\npublic interface ITileDataFactory {\n    /**\n     * Creates a new {@link ITileEntityData} for the given tileEntity if supported by this {@code ITileDataFactory}.\n     * @param tileEntity The {@link TileEntity} to provide {@link ITileEntityData} for.\n     * @return A new {@link ITileEntityData} if this {@code ITileDataFactory} can create one for the given tileEntity or null if not.\n     */\n    @Nullable\n    ITileEntityData createDataFor(BlockEntity tileEntity);\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/building/tilesupport/ITileDataProvider.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.building.tilesupport;\n\n/**\n * Represents an {@link java.util.function.Supplier Supplier&lt;ITileEntityData&gt;} which can be used for creating {@link ITileEntityData} instances.\n * <p>\n * A {@link net.minecraft.tileentity.TileEntity} implementing this interface will be queried from {@link TileSupport#dataProviderFactory()} in order to create {@link ITileEntityData}.\n * It may still choose to return null.\n */\n@FunctionalInterface\npublic interface ITileDataProvider {\n    /**\n     * @return A new {@link ITileEntityData} or null if non should be created.\n     */\n    ITileEntityData createTileData();\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/building/tilesupport/ITileDataSerializer.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.building.tilesupport;\n\nimport net.minecraft.nbt.CompoundTag;\n\n/**\n * This class represents a serializer responsible for converting {@link ITileEntityData} into {@link CompoundNBT} and back. Of course an {@code ITileDataSerializer}\n * can only support deserialization for one specific {@link ITileEntityData} implementation and therefore all methods in this class are expected\n * to throw {@link IllegalArgumentException} if faced with an implementation, which is not supported by this {@code ITileEntityDataSerializer}.\n * <p>\n * Additionally the {@link #serialize(ITileEntityData, boolean)} and {@link #deserialize(CompoundNBT, boolean)} methods are passed an boolean flag in order to hint\n * whether the save is going to be persisted. This flag is provided to allow for less bandwidth to be used when sending large {@link ITileEntityData}'s over the Network.\n */\npublic interface ITileDataSerializer {\n    /**\n     * Serializes a given {@link ITileEntityData}. The persisted flag is meant to allow for more efficient usage of a Player's Network capabilities by using for example\n     * RegistryId's for serialisation instead of RegistryNames.\n     *\n     * @param data      The {@link ITileEntityData} to serialize.\n     * @param persisted Whether or not this data is meant to be persisted.\n     * @return A {@link CompoundNBT} representing the serialized form of the given {@link ITileEntityData}. May be empty but <b>not null</b> to\n     * indicate that no data requires serialization.\n     * @throws IllegalArgumentException If the {@link ITileEntityData} implementation is not supported by this serializer or\n     *                                  if a persisted save is attempted to be retrieved non-persistently or vice-versa.\n     */\n    CompoundTag serialize(ITileEntityData data, boolean persisted);\n\n    /**\n     * Deserializes an {@link ITileEntityData} from the given {@link CompoundNBT}.\n     *\n     * @param tagCompound The {@link CompoundNBT} to deserialize an {@link ITileEntityData} for.\n     * @param persisted   Whether or not this data was previously persisted. It can be expected that this flag matches\n     *                    whatever was passed into {@link #serialize(ITileEntityData, boolean)} to create the {@link CompoundNBT}.\n     * @return The {@link ITileEntityData} representing the serialized data in the {@link CompoundNBT}.\n     * @throws IllegalArgumentException If the data does not match the format of this serializer or if a persisted save is attempted to be retrieved non-persistently or vice-versa.\n     */\n    ITileEntityData deserialize(CompoundTag tagCompound, boolean persisted);\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/building/tilesupport/ITileEntityData.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.building.tilesupport;\n\nimport com.direwolf20.buildinggadgets.common.BuildingGadgets;\nimport com.direwolf20.buildinggadgets.common.tainted.building.view.BuildContext;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.materials.MaterialList;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.materials.objects.UniqueItem;\nimport com.google.common.collect.Multiset;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.phys.HitResult;\n\nimport javax.annotation.Nullable;\n\n/**\n * Represents the serializable data of an {@link net.minecraft.tileentity.TileEntity}. It also provides actions which can be performed on the\n * underlying data, to either:\n * <ul>\n * <li>Check whether placement should be permitted via {@link #allowPlacement(BuildContext, BlockState, BlockPos)}\n * <li>Place this data with a given {@link BlockState} in an {@link BuildContext} via {@link #placeIn(BuildContext, BlockState, BlockPos)}\n * <li>Query the requiredItems to build this {@link ITileEntityData} via {@link #getRequiredItems(BuildContext, BlockState, RayTraceResult, BlockPos)}.\n * </ul>\n */\npublic interface ITileEntityData {\n    /**\n     * @return The {@link ITileDataSerializer} which can be used for serializing this {@link ITileEntityData} instance.\n     */\n    ITileDataSerializer getSerializer();\n\n    /**\n     * Attempts to place this {@link ITileEntityData} in the given {@link BuildContext}. If this is called but {@link #allowPlacement(BuildContext, BlockState, BlockPos)}\n     * would have returned false, placement should still be attempted and counted as a \"forced placement\".<br>\n     * This Method should also set any data on the {@link net.minecraft.world.level.block.entity.BlockEntity} represented by this {@code ITileEntityData}.\n     * @param context The {@link BuildContext} to place in.\n     * @param state The {@link BlockState} to place.\n     * @param position The {@link BlockPos} at which to place\n     * @return Whether or not placement was performed by this {@link ITileEntityData}. This should only return false if some really hard requirements would not be met,\n     *         like for example a required block not being present next to the given position.\n     */\n    boolean placeIn(BuildContext context, BlockState state, BlockPos position);\n\n    /**\n     * @param context The context in which to query required items.\n     * @param state The {@link BlockState} to retrieve items for\n     * @param target {@link HitResult} the target at which a click is simulated\n     * @param pos The {@link BlockPos} where a block is simulated for this Method\n     * @return A {@link Multiset} of required Items.\n     */\n    default MaterialList getRequiredItems(BuildContext context, BlockState state, @Nullable HitResult target, @Nullable BlockPos pos) {\n        ItemStack stack = null;\n        try {\n            stack = state.getCloneItemStack(target, context.getWorld(), pos, context.getPlayer());\n        } catch (Exception e) {\n            BuildingGadgets.LOG.trace(\"Failed to retrieve pickBlock for {}.\", state, e);\n        }\n\n        if (stack == null)\n            stack = new ItemStack(state.getBlock());\n\n        if (stack.isEmpty())\n            return MaterialList.empty();\n\n        return MaterialList.of(UniqueItem.ofStack(stack));\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/building/tilesupport/NBTTileEntityData.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.building.tilesupport;\n\nimport com.direwolf20.buildinggadgets.common.BuildingGadgets;\nimport com.direwolf20.buildinggadgets.common.tainted.building.view.BuildContext;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.materials.MaterialList;\nimport com.direwolf20.buildinggadgets.common.tainted.registry.Registries;\nimport com.google.common.base.MoreObjects;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.world.level.block.entity.BlockEntity;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.phys.HitResult;\n\nimport javax.annotation.Nonnull;\nimport javax.annotation.Nullable;\nimport java.util.Objects;\n\npublic class NBTTileEntityData implements ITileEntityData {\n    public static NBTTileEntityData ofTile(BlockEntity te) {\n        CompoundTag nbt = te.saveWithoutMetadata();\n        return new NBTTileEntityData(nbt);\n    }\n\n    @Nonnull\n    private final CompoundTag nbt;\n    @Nullable\n    private final MaterialList requiredMaterials;\n\n    public NBTTileEntityData(CompoundTag nbt, @Nullable MaterialList requiredMaterials) {\n        this.nbt = Objects.requireNonNull(nbt);\n        this.requiredMaterials = requiredMaterials;\n    }\n\n    public NBTTileEntityData(CompoundTag nbt) {\n        this(nbt, null);\n    }\n\n    @Override\n    public ITileDataSerializer getSerializer() {\n        return Registries.NBT_TILE_DATA_SERIALIZER.get();\n    }\n\n    @Override\n    public MaterialList getRequiredItems(BuildContext context, BlockState state, @Nullable HitResult target, @Nullable BlockPos pos) {\n        if (requiredMaterials != null)\n            return requiredMaterials;\n        return ITileEntityData.super.getRequiredItems(context, state, target, pos);\n    }\n\n    @Override\n    public boolean placeIn(BuildContext context, BlockState state, BlockPos position) {\n        BuildingGadgets.LOG.trace(\"Placing {} with Tile NBT at {}.\", state, position);\n        context.getWorld().setBlock(position, state, 0);\n        BlockEntity te = context.getWorld().getBlockEntity(position);\n        if (te != null) {\n            try {\n                te.deserializeNBT(getNBTModifiable());\n            } catch (Exception e) {\n                BuildingGadgets.LOG.debug(\"Failed to apply Tile NBT Data to {} at {} in Context {}\", state, position, context, e);\n            }\n        }\n        return true;\n    }\n\n    public CompoundTag getNBT() {\n        return nbt.copy();\n    }\n\n    @Nullable\n    public MaterialList getRequiredMaterials() {\n        return requiredMaterials;\n    }\n\n    protected CompoundTag getNBTModifiable() {\n        return nbt;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) return true;\n        if (!(o instanceof NBTTileEntityData)) return false;\n\n        NBTTileEntityData that = (NBTTileEntityData) o;\n\n        if (!nbt.equals(that.nbt)) return false;\n        return getRequiredMaterials() != null ? getRequiredMaterials().equals(that.getRequiredMaterials()) : that.getRequiredMaterials() == null;\n    }\n\n    @Override\n    public int hashCode() {\n        int result = nbt.hashCode();\n        result = 31 * result + (getRequiredMaterials() != null ? getRequiredMaterials().hashCode() : 0);\n        return result;\n    }\n\n    @Override\n    public String toString() {\n        return MoreObjects.toStringHelper(this)\n                .add(\"nbt\", nbt)\n                .add(\"requiredMaterials\", requiredMaterials)\n                .toString();\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/building/tilesupport/TileSupport.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.building.tilesupport;\n\nimport com.direwolf20.buildinggadgets.common.tainted.building.BlockData;\nimport com.direwolf20.buildinggadgets.common.tainted.building.view.BuildContext;\nimport com.direwolf20.buildinggadgets.common.tainted.registry.Registries.TileEntityData;\nimport com.direwolf20.buildinggadgets.common.tainted.template.SerialisationSupport;\nimport com.google.common.base.MoreObjects;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.level.block.entity.BlockEntity;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.world.level.BlockGetter;\n\nimport javax.annotation.Nullable;\nimport java.util.Iterator;\nimport java.util.Objects;\n\npublic final class TileSupport {\n    private TileSupport() {}\n\n    private static ITileDataFactory DATA_PROVIDER_FACTORY = new DataProviderFactory();\n\n    /**\n     * Returns an adapter for {@link ITileDataFactory} and {@link ITileDataProvider}. If a {@link TileEntity} is an instance of {@link ITileDataProvider} this {@link ITileDataFactory}\n     * will return the data created by the given {@link ITileDataProvider}.\n     * <p>\n     * Notice that this will by default be registered to be the last {@link ITileDataFactory} called in order to allow mods to override the data returned by a {@link TileEntity} itself.\n     *\n     * @return An {@link ITileDataFactory} which will return {@link ITileEntityData} instances provided by {@link TileEntity TileEntities} implementing {@link ITileDataProvider}\n     */\n    public static ITileDataFactory dataProviderFactory() {\n        return DATA_PROVIDER_FACTORY;\n    }\n\n    public static ITileEntityData createTileData(@Nullable BlockEntity te) {\n        if (te == null)\n            return dummyTileEntityData();\n        ITileEntityData res;\n\n        for (Iterator<ITileDataFactory> it = TileEntityData.getTileDataFactories().iterator(); it.hasNext(); ) {\n            ITileDataFactory factory = it.next();\n            res = factory.createDataFor(te);\n            if (res != null)\n                return res;\n        }\n\n        return dummyTileEntityData();\n    }\n\n    public static ITileEntityData createTileData(BlockGetter world, BlockPos pos) {\n        BlockEntity te = world.getBlockEntity(pos);\n        return createTileData(te);\n    }\n\n    public static BlockData createBlockData(BlockState state, @Nullable BlockEntity te) {\n        return new BlockData(Objects.requireNonNull(state), createTileData(te));\n    }\n\n    public static BlockData createBlockData(BlockGetter world, BlockPos pos) {\n        return new BlockData(world.getBlockState(pos), createTileData(world, pos));\n    }\n\n    private static class DataProviderFactory implements ITileDataFactory {\n        @Nullable\n        @Override\n        public ITileEntityData createDataFor(BlockEntity tileEntity) {\n            if (tileEntity instanceof ITileDataProvider)\n                return ((ITileDataProvider) tileEntity).createTileData();\n            return null;\n        }\n    }\n\n    private static final ITileEntityData DUMMY_TILE_ENTITY_DATA = new ITileEntityData() {\n        @Override\n        public ITileDataSerializer getSerializer() {\n            return SerialisationSupport.dummyDataSerializer();\n        }\n\n        @Override\n        public boolean placeIn(BuildContext context, BlockState state, BlockPos position) {\n            return context.getWorld().setBlock(position, state, 0);\n        }\n\n        @Override\n        public String toString() {\n            return MoreObjects.toStringHelper(this)\n                    .toString();\n        }\n    };\n\n    public static ITileEntityData dummyTileEntityData() {\n        return DUMMY_TILE_ENTITY_DATA;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/building/tilesupport/package-info.java",
    "content": "/**\n * Provides classes for handling {@link net.minecraft.tileentity.TileEntity TileEntities} by associating an {@link net.minecraft.tileentity.TileEntity} with a\n * specific {@link com.direwolf20.buildinggadgets.common.tainted.building.tilesupport.ITileEntityData}. These can be created using\n * {@link com.direwolf20.buildinggadgets.common.tainted.building.tilesupport.ITileDataFactory ITileDataFactories} or as a specialisation of these\n * {@link com.direwolf20.buildinggadgets.common.tainted.building.tilesupport.ITileDataProvider ITileDataProviders}.\n */\n@ParametersAreNonnullByDefault\n@MethodsReturnNonnullByDefault\npackage com.direwolf20.buildinggadgets.common.tainted.building.tilesupport;\n\nimport net.minecraft.MethodsReturnNonnullByDefault;\n\nimport javax.annotation.ParametersAreNonnullByDefault;"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/building/view/BuildContext.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.building.view;\n\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.ServerLevelAccessor;\nimport net.minecraft.world.level.LevelAccessor;\nimport net.minecraft.server.level.ServerLevel;\n\nimport javax.annotation.Nonnull;\nimport javax.annotation.Nullable;\nimport javax.annotation.concurrent.Immutable;\nimport java.util.Objects;\n\n/**\n * Simple implementation of {@link BuildContext} providing a {@link Builder} for creation.\n */\n@Immutable\npublic final class BuildContext {\n    /**\n     * @return A new {@link Builder}.\n     */\n    public static Builder builder() {\n        return new Builder();\n    }\n\n    @Nonnull\n    private final LevelAccessor world;\n    @Nullable\n    private final Player player;\n\n    private final ItemStack stack;\n\n    public BuildContext(@Nonnull LevelAccessor world, @Nullable Player player, @Nonnull ItemStack stack) {\n        this.world = world;\n        this.player = player;\n        this.stack = stack;\n    }\n\n    /**\n     * @return The {@link IWorld} of this {@code SimpleBuildContext}. Will not be null.\n     */\n    public LevelAccessor getWorld() {\n        return world;\n    }\n\n    /**\n     * @return The {@link PlayerEntity} performing the build. May be null if unknown.\n     */\n    @Nullable\n    public Player getPlayer() {\n        return player;\n    }\n\n    public ItemStack getStack() {\n        return stack;\n    }\n\n    public ServerLevel getServerWorld() {\n        return ((ServerLevelAccessor) world).getLevel();\n    }\n\n    /**\n     * {@code SimpleBuilder} for creating new instances of {@link BuildContext}\n     */\n    public static final class Builder {\n        @Nullable\n        private LevelAccessor world;\n        @Nullable\n        private Player buildingPlayer;\n        @Nonnull\n        private ItemStack stack;\n\n        private Builder() {\n            this.world = null;\n            this.buildingPlayer = null;\n            this.stack = ItemStack.EMPTY;\n        }\n\n        /**\n         * Sets the {@link IWorld} of the resulting {@link BuildContext}.\n         * @param world The {@link IWorld} of the resulting {@link BuildContext}.\n         * @return The {@code Builder} itself\n         * @see BuildContext#getWorld()\n         */\n        public Builder world(@Nonnull LevelAccessor world) {\n            this.world = world;\n            return this;\n        }\n\n        /**\n         * Sets the {@link PlayerEntity} of the resulting {@link BuildContext}. Notice that this also set's the world\n         * for the resulting {@code SimpleBuildContext} if the player is non-null and a world hasn't been set yet.\n         * <p>\n         * This defaults to null.\n         * @param buildingPlayer The {@link PlayerEntity} of the resulting {@link BuildContext}.\n         * @return The {@code Builder} itself\n         * @see BuildContext#getPlayer()\n         */\n        public Builder player(@Nullable Player buildingPlayer) {\n            this.buildingPlayer = buildingPlayer;\n            if (world == null && buildingPlayer != null)\n                this.world = buildingPlayer.level;\n            return this;\n        }\n\n        /**\n         * Sets the {@link ItemStack} of the resulting {@link BuildContext}.\n         * <p>\n         * Defaults to {@link ItemStack#EMPTY}.\n         *\n         * @param stack The {@link ItemStack} of the resulting {@code SimpleBuildContext}\n         * @return The {@code Builder} itself\n         * @see BuildContext#getStack()\n         */\n        public Builder stack(@Nonnull ItemStack stack) {\n            this.stack = stack;\n            return this;\n        }\n\n        /**\n         * Creates a new {@link BuildContext} using the world previously set on this {@code Builder}.\n         * @return A new {@link BuildContext} with the values specified in this {@code Builder}.\n         * @see #build(IWorld)\n         */\n        public BuildContext build() {\n            return build(null);\n        }\n\n        /**\n         * Creates a new {@link BuildContext} using the specified world. If the given world is null, the world in this {@code Builder} will be used.\n         * @param world The {@link IWorld} to use. If null this {@code SimpleBuilder}'s world will be used.\n         * @return A new {@link BuildContext} with the values specified in this {@code SimpleBuilder}.\n         * @throws NullPointerException if both the {@link World} passed in and the {@link World} of this {@code Builder} are null.\n         */\n        public BuildContext build(@Nullable LevelAccessor world) {\n            return new BuildContext(world != null ? world : Objects.requireNonNull(this.world), buildingPlayer, stack);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/building/view/IBuildView.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.building.view;\n\nimport com.direwolf20.buildinggadgets.common.tainted.building.PlacementTarget;\nimport com.direwolf20.buildinggadgets.common.tainted.building.Region;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.materials.MaterialList;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.materials.objects.IUniqueObject;\nimport com.direwolf20.buildinggadgets.common.tainted.template.Template;\nimport com.direwolf20.buildinggadgets.common.util.CommonUtils;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.world.phys.Vec3;\n\nimport javax.annotation.Nullable;\nimport java.util.Iterator;\nimport java.util.Spliterator;\nimport java.util.Spliterators;\n\n/**\n * A \"snapshot\" view of a specific buildable TemplateItem providing the ability to iterate over the represented {@link PlacementTarget}'s.\n * It also allows for translating to a specific position via {@link #translateTo(BlockPos)}.<br>\n * Furthermore an {@code IBuildView} should provide a hint for users to check the amount of blocks an {@link IBuildView} of this {@code TemplateItem} is going to\n * produce at most via {@link #estimateSize()} in combination with hinting the amount of {@link IUniqueObject}'s required.\n * However this is not strictly necessary and when computation might be costly it is not advised to return an accurate value.\n * <p>\n * The {@code IBuildView} is constructed given an instance of {@link BuildContext}. This\n * context allows the {@link IBuildView} to adapt itself to the environment in which it is viewed. Therefore no assumptions may be made, that\n * 2 distinct instances of {@code IBuildView} will produce the same results even if they were constructed by the same {@link BuildContext}.\n * <p>\n * All Methods in this class may throw an {@link IllegalStateException} if called after the {@code IBuildView} has been closed.\n * @implSpec Notice that no guarantees are made for the order in which {@link PlacementTarget}'s are produced by this {@code IBuildView}.\n * Order may be arbitrary or sorted, consult the documentation of the implementation you are currently faced with for information about traversal order.\n */\npublic interface IBuildView extends Iterable<PlacementTarget> {\n    /**\n     * @return An {@link Iterator} over the {@link PlacementTarget}'s of this {@code IBuildView}.\n     * @implNote The default implementation is equivalent to calling {@code Spliterators.iterator(view.spliterator());}.\n     */\n    @Override\n    default Iterator<PlacementTarget> iterator() {\n        return Spliterators.iterator(spliterator());\n    }\n\n    /**\n     * @return An {@link Spliterator} over the {@link PlacementTarget}'s of this {@code IBuildView}.\n     */\n    @Override\n    Spliterator<PlacementTarget> spliterator();\n\n    /**\n     * Translates this {@code IBuildView} to the specified position.\n     *\n     * @param pos The position to translate to. May not be null.\n     * @return The new translated {@code IBuildView}. May be the same or a new instance depending on implementation.\n     * @throws NullPointerException if the given Position was null\n     * @implSpec This Method may not accumulate multiple translations, but instead always set the absolute Translation performed\n     * to the specified value.\n     */\n    IBuildView translateTo(BlockPos pos);\n\n    /**\n     * Attempts to compute the amount of required {@link IUniqueObject}'s. Should never be more than might be needed,\n     * but may be fewer if exact requirements are hard or expensive to compute.\n     *\n     * @param simulatePos nullable BlockPos used to simulate\n     * @return A {@link MaterialList} representing the Item Requirements to build this {@code IBuildView}.\n     */\n    default MaterialList estimateRequiredItems(@Nullable Vec3 simulatePos) {\n        return CommonUtils.estimateRequiredItems(this, this.getContext(), simulatePos);\n    }\n\n    default MaterialList estimateRequiredItems() {\n        Player player = getContext().getPlayer();\n        return estimateRequiredItems(player != null ? player.position() : null);\n    }\n\n    /**\n     * Attempts to compute an estimate of how many {@link PlacementTarget}'s this {@code IBuildView} will produce.\n     * Should never be smaller than the amount produced by iterating over this, but may be larger if an exact size\n     * is hard or expensive to compute.\n     * <p>\n     * A negative value indicates that the size cannot be determined easily.\n     *\n     * @return A prediction of how many {@link PlacementTarget} this {@code TemplateItem} is going to produce.\n     * Negative if unknown or expensive to compute.\n     */\n    int estimateSize();\n\n    /**\n     * Performs a deep copy of this {@code TemplateView} iterating over all positions if necessary. The resulting {@code TemplateView} should not care about\n     * the behaviour of the backing {@link Template} and instead be independent of any resource lock's this {@code TemplateView} imposes as well as not imposing any\n     * resource locks itself.\n     * <p>\n     * Calling\n     * {@code\n     * IBuildView view = template.createViewInContext(ctx);\n     * IBuildView copy = view.copy();\n     * view.close();\n     * }\n     * should ensure that the {@link Template} is no longer restricted because of an open {@code TemplateView}, whilst at the same time providing access to all the positions via\n     * {@code copy} in the original {@code TemplateView} in a non-lazy manner.\n     * <p>\n     * <b>However: be warned that this might require O(n) execution time for this Method on some implementations (with n being the total number of\n     * {@link PlacementTarget PlacementTargets} produced) and will almost certainly nullify any benefits that the original {@code TemplateView} may have had by\n     * using a lazy implementation.</b>\n     *\n     * @return A full copy of this {@code TemplateView}. Iterating over the whole {@code TemplateView} if necessary.\n     */\n    IBuildView copy();\n\n    Region getBoundingBox();\n\n    BuildContext getContext();\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/building/view/PositionalBuildView.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.building.view;\n\nimport com.direwolf20.buildinggadgets.common.tainted.building.BlockData;\nimport com.direwolf20.buildinggadgets.common.tainted.building.PlacementTarget;\nimport com.direwolf20.buildinggadgets.common.tainted.building.Region;\nimport com.direwolf20.buildinggadgets.common.util.spliterator.MappingSpliterator;\nimport com.google.common.collect.ImmutableMap;\nimport net.minecraft.core.BlockPos;\n\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Spliterator;\nimport java.util.function.Function;\n\n/**\n * A simple {@link IBuildView} backed by a {@link Map Map<BlockPos, BlockData>}. {@link PlacementTarget PlacementTargets} will be created\n * lazily when iterating over this {@link IBuildView}. You can supply this with a mutable {@link Map} via {@link #createUnsafe(BuildContext, Map, Region)}\n * for efficiency reasons, note however that you will encounter undefined behaviour if the {@link Map} is modified after this {@link IBuildView} was\n * created.\n */\npublic final class PositionalBuildView implements IBuildView {\n    private Map<BlockPos, BlockData> map;\n    private Region boundingBox;\n    private BlockPos translation;\n    private BuildContext context;\n\n\n    public static PositionalBuildView createUnsafe(BuildContext context, Map<BlockPos, BlockData> map, Region boundingBox) {\n        return new PositionalBuildView(\n                Objects.requireNonNull(context, \"Cannot have a PositionalBuildView without BuildContext!\"),\n                Objects.requireNonNull(map, \"Cannot have a PositionalBuildView without position to data map!\"),\n                Objects.requireNonNull(boundingBox, \"Cannot have a PositionalBuildView without a boundingBox!\")\n        );\n    }\n\n    private PositionalBuildView(BuildContext context, Map<BlockPos, BlockData> map, Region boundingBox) {\n        this.context = context;\n        this.map = map;\n        this.boundingBox = boundingBox;\n        this.translation = BlockPos.ZERO;\n    }\n\n    @Override\n    public Spliterator<PlacementTarget> spliterator() {\n        BlockPos translation = this.translation;\n        return new MappingSpliterator<>(map.entrySet().spliterator(), e -> new PlacementTarget(e.getKey().offset(translation), e.getValue()));\n    }\n\n    @Override\n    public PositionalBuildView translateTo(BlockPos pos) {\n        boundingBox = boundingBox.translate(pos.subtract(translation));//translate the bounding box to the correct position\n        this.translation = pos;\n        return this;\n    }\n\n    @Override\n    public int estimateSize() {\n        return map.size();\n    }\n\n\n    @Override\n    public PositionalBuildView copy() {\n        return new PositionalBuildView(context, map, boundingBox);\n    }\n\n    @Override\n    public Region getBoundingBox() {\n        return this.boundingBox;\n    }\n\n    @Override\n    public BuildContext getContext() {\n        return context;\n    }\n\n    public ImmutableMap<BlockPos, BlockData> getMap() {\n        return ImmutableMap.copyOf(map);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/building/view/WorldBuildView.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.building.view;\n\nimport com.direwolf20.buildinggadgets.common.tainted.building.BlockData;\nimport com.direwolf20.buildinggadgets.common.tainted.building.PlacementTarget;\nimport com.direwolf20.buildinggadgets.common.tainted.building.Region;\nimport com.direwolf20.buildinggadgets.common.tainted.building.tilesupport.TileSupport;\nimport com.direwolf20.buildinggadgets.common.util.spliterator.DelegatingSpliterator;\nimport net.minecraft.core.BlockPos;\n\nimport javax.annotation.Nullable;\nimport java.util.Comparator;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.Spliterator;\nimport java.util.function.BiFunction;\nimport java.util.function.Consumer;\n\n/**\n * An {@link IBuildView} which views a {@link Region} in an {@link IWorld} as an {@link IBuildView}. {@link PlacementTarget PlacementTargets}\n * will be created lazily when iterating over this {@link IBuildView} via {@code new PlacementTarget(pos, TileSupport.createBlockData(world, pos))}\n * where pos is the Position currently iterating on and world is the world provided by this views {@link #getContext() build context}.\n * <p>\n * This {@link IBuildView} is especially useful, when trying to read all {@link BlockData} instances with in a given {@link Region}.\n * If you need this Information in a pre-determined way, or intend on iterating multiple times on this {@link IBuildView} consider\n * calling {@link #evaluate()} (which is equivalent to calling {@code PositionalBuildView.ofIterable(view.getContext(), view)})\n * to evaluate all {@link BlockData} instances described by this {@link IBuildView view}.\n */\npublic final class WorldBuildView implements IBuildView {\n    private final BuildContext context;\n    private final Region region;\n    private final BiFunction<BuildContext, BlockPos, Optional<BlockData>> dataFactory;\n    private BlockPos translation;\n\n    public static WorldBuildView create(BuildContext context, Region region) {\n        return create(context, region, null);\n    }\n\n    public static WorldBuildView create(BuildContext context, Region region, @Nullable BiFunction<BuildContext, BlockPos, Optional<BlockData>> dataFactory) {\n        return new WorldBuildView(\n                Objects.requireNonNull(context, \"Cannot create WorldBuildView without an BuildContext!\"),\n                Objects.requireNonNull(region, \"Cannot create WorldBuildView without an Region!\"),\n                dataFactory != null ? dataFactory : (c, p) -> Optional.of(TileSupport.createBlockData(c.getWorld(), p)));\n    }\n\n    private WorldBuildView(BuildContext context, Region region, BiFunction<BuildContext, BlockPos, Optional<BlockData>> dataFactory) {\n        this.context = context;\n        this.region = region;\n        this.dataFactory = dataFactory;\n        this.translation = BlockPos.ZERO;\n    }\n\n    @Override\n    public Spliterator<PlacementTarget> spliterator() {\n        return new WorldBackedSpliterator(getBoundingBox().stream().spliterator(), translation, dataFactory, getContext());\n    }\n\n    @Override\n    public WorldBuildView translateTo(BlockPos pos) {\n        this.translation = pos;\n        return this;\n    }\n\n    @Override\n    public int estimateSize() {\n        return region.size();\n    }\n\n    @Override\n    public WorldBuildView copy() {\n        return new WorldBuildView(getContext(), getBoundingBox(), dataFactory);\n    }\n\n    @Override\n    public BuildContext getContext() {\n        return context;\n    }\n\n    public Region getBoundingBox() {\n        return region;\n    }\n\n    private static final class WorldBackedSpliterator extends DelegatingSpliterator<BlockPos, PlacementTarget> {\n        private final BlockPos translation;\n        private final BiFunction<BuildContext, BlockPos, Optional<BlockData>> dataFactory;\n        private final BuildContext context;\n\n        private WorldBackedSpliterator(Spliterator<BlockPos> other, BlockPos translation, BiFunction<BuildContext, BlockPos, Optional<BlockData>> dataFactory, BuildContext context) {\n            super(other);\n            this.translation = translation;\n            this.dataFactory = dataFactory;\n            this.context = context;\n        }\n\n        @Override\n        protected boolean advance(BlockPos object, Consumer<? super PlacementTarget> action) {\n            Optional<BlockData> dataOptional = dataFactory.apply(context, object);\n            if (dataOptional.isPresent()) {\n                BlockData data = dataOptional.get();\n                action.accept(new PlacementTarget(object.offset(translation), data));\n                return true;\n            }\n            return false;\n        }\n\n        @Override\n        public Comparator<? super PlacementTarget> getComparator() {\n            return Comparator.comparing(PlacementTarget::getPos);\n        }\n\n        @Override\n        @Nullable\n        public Spliterator<PlacementTarget> trySplit() {\n            Spliterator<BlockPos> other = getOther().trySplit();\n            if (other != null)\n                return new WorldBackedSpliterator(other, translation, dataFactory, context);\n            return null;\n        }\n\n        @Override\n        public int characteristics() {\n            return ORDERED | SORTED | DISTINCT;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/building/view/package-info.java",
    "content": "@ParametersAreNonnullByDefault\n@MethodsReturnNonnullByDefault\npackage com.direwolf20.buildinggadgets.common.tainted.building.view;\n\nimport net.minecraft.MethodsReturnNonnullByDefault;\n\nimport javax.annotation.ParametersAreNonnullByDefault;"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/concurrent/CopyScheduler.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.concurrent;\n\nimport com.direwolf20.buildinggadgets.common.items.GadgetCopyPaste;\nimport com.direwolf20.buildinggadgets.common.items.OurItems;\nimport com.direwolf20.buildinggadgets.common.tainted.building.BlockData;\nimport com.direwolf20.buildinggadgets.common.tainted.building.PlacementTarget;\nimport com.direwolf20.buildinggadgets.common.tainted.building.Region;\nimport com.direwolf20.buildinggadgets.common.tainted.building.view.BuildContext;\nimport com.direwolf20.buildinggadgets.common.tainted.building.view.IBuildView;\nimport com.google.common.base.Preconditions;\nimport com.google.common.collect.ImmutableMap;\nimport net.minecraft.core.BlockPos;\n\nimport java.util.Objects;\nimport java.util.Spliterator;\nimport java.util.function.BiConsumer;\n\npublic final class CopyScheduler extends SteppedScheduler {\n    public static void scheduleCopy(BiConsumer<ImmutableMap<BlockPos, BlockData>, Region> finisher, IBuildView worldView, int steps) {\n        Preconditions.checkArgument(steps > 0);\n        ServerTickingScheduler.runTicked(new CopyScheduler(\n                Objects.requireNonNull(finisher),\n                Objects.requireNonNull(worldView),\n                steps\n        ));\n    }\n\n    private final BiConsumer<ImmutableMap<BlockPos, BlockData>, Region> finisher;\n    private final Spliterator<PlacementTarget> targets;\n    private final ImmutableMap.Builder<BlockPos, BlockData> builder;\n    private Region.Builder regionBuilder;\n    private final BuildContext context;\n\n    private CopyScheduler(BiConsumer<ImmutableMap<BlockPos, BlockData>, Region> finisher, IBuildView worldView, int steps) {\n        super(steps);\n        this.finisher = finisher;\n        this.targets = worldView.spliterator();\n        this.builder = ImmutableMap.builder();\n        this.context = worldView.getContext();\n        this.regionBuilder = null;\n    }\n\n    @Override\n    protected StepResult advance() {\n        return StepResult.ofBoolean(targets.tryAdvance(t -> {\n            if (!t.getData().getState().isAir() && ((GadgetCopyPaste) OurItems.COPY_PASTE_GADGET_ITEM.get()).isAllowedBlock(t.getData().getState())) {\n                builder.put(t.getPos(), t.getData());\n                if (regionBuilder == null)\n                    regionBuilder = Region.enclosingBuilder();\n                regionBuilder.enclose(t.getPos());\n            }\n        }));\n    }\n\n    @Override\n    protected void onFinish() {\n        finisher.accept(builder.build(), regionBuilder != null ? regionBuilder.build() : Region.singleZero());\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/concurrent/PlacementScheduler.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.concurrent;\n\nimport com.direwolf20.buildinggadgets.common.blocks.EffectBlock;\nimport com.direwolf20.buildinggadgets.common.blocks.EffectBlock.Mode;\nimport com.direwolf20.buildinggadgets.common.tainted.building.BlockData;\nimport com.direwolf20.buildinggadgets.common.tainted.building.PlacementChecker;\nimport com.direwolf20.buildinggadgets.common.tainted.building.PlacementChecker.CheckResult;\nimport com.direwolf20.buildinggadgets.common.tainted.building.PlacementTarget;\nimport com.direwolf20.buildinggadgets.common.tainted.building.tilesupport.TileSupport;\nimport com.direwolf20.buildinggadgets.common.tainted.building.view.BuildContext;\nimport com.direwolf20.buildinggadgets.common.tainted.building.view.IBuildView;\nimport com.direwolf20.buildinggadgets.common.tainted.save.Undo;\nimport com.direwolf20.buildinggadgets.common.tainted.save.Undo.Builder;\nimport com.google.common.base.Preconditions;\nimport net.minecraft.world.level.block.DoorBlock;\nimport net.minecraft.world.level.block.state.properties.BlockStateProperties;\nimport net.minecraft.world.level.block.state.properties.DoubleBlockHalf;\n\nimport java.util.Objects;\nimport java.util.Spliterator;\nimport java.util.function.Consumer;\n\npublic final class PlacementScheduler extends SteppedScheduler {\n    public static PlacementScheduler schedulePlacement(IBuildView view, PlacementChecker checker, int steps) {\n        Preconditions.checkArgument(steps > 0);\n\n        PlacementScheduler res = new PlacementScheduler(\n                Objects.requireNonNull(view),\n                Objects.requireNonNull(checker),\n                steps);\n\n        ServerTickingScheduler.runTicked(res);\n        return res;\n    }\n\n    private final IBuildView view;\n    private final Spliterator<PlacementTarget> spliterator;\n    private final PlacementChecker checker;\n    private boolean lastWasSuccess;\n    private Consumer<PlacementScheduler> finisher;\n    private Undo.Builder undoBuilder;\n\n    private PlacementScheduler(IBuildView view, PlacementChecker checker, int steps) {\n        super(steps);\n        this.checker = checker;\n        this.view = view;\n        this.spliterator = view.spliterator();\n        this.undoBuilder = Undo.builder();\n        this.finisher = p -> {};\n    }\n    @Override\n    protected void onFinish() {\n        finisher.accept(this);\n    }\n\n    @Override\n    protected StepResult advance() {\n        if (! spliterator.tryAdvance(this::checkTarget))\n            return StepResult.END;\n        return lastWasSuccess ? StepResult.SUCCESS : StepResult.FAILURE;\n    }\n\n    public Builder getUndoBuilder() {\n        return undoBuilder;\n    }\n\n    public PlacementScheduler withFinisher(Consumer<PlacementScheduler> runnable) {\n        this.finisher = Objects.requireNonNull(runnable);\n        return this;\n    }\n\n    private void checkTarget(PlacementTarget target) {\n        CheckResult res = checker.checkPositionWithResult(view.getContext(), target, false);\n        lastWasSuccess = res.isSuccess();\n        if (lastWasSuccess) {\n            undoBuilder.record(view.getContext().getWorld(), target.getPos(), target.getData(), res.getMatch().getChosenOption(), res.getInsertedItems());\n            EffectBlock.spawnEffectBlock(view.getContext(), target, Mode.PLACE, res.isUsingPaste());\n\n            BuildContext context = view.getContext();\n            BlockData targetBlock = target.getData();\n            if (target.getData().getState().getBlock() instanceof DoorBlock && targetBlock.getState().getValue(BlockStateProperties.DOUBLE_BLOCK_HALF) == DoubleBlockHalf.LOWER && context.getWorld().isEmptyBlock(target.getPos().above())) {\n                EffectBlock.spawnEffectBlock(context.getWorld(), target.getPos().above(), new BlockData(targetBlock.getState().setValue(BlockStateProperties.DOUBLE_BLOCK_HALF, DoubleBlockHalf.UPPER), TileSupport.dummyTileEntityData()), Mode.PLACE, false);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/concurrent/ServerTickingScheduler.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.concurrent;\n\nimport net.minecraftforge.common.MinecraftForge;\nimport net.minecraftforge.event.TickEvent.Phase;\nimport net.minecraftforge.event.TickEvent.ServerTickEvent;\nimport net.minecraftforge.eventbus.api.SubscribeEvent;\n\nimport java.util.EnumSet;\nimport java.util.function.BooleanSupplier;\n\npublic final class ServerTickingScheduler {\n    public static void runTicked(BooleanSupplier runUntilFalse) {\n        new ServerTickingScheduler(runUntilFalse, EnumSet.of(Phase.START));\n    }\n\n    public static void runTickedAtEnd(BooleanSupplier runUntilFalse) {\n        new ServerTickingScheduler(runUntilFalse, EnumSet.of(Phase.END));\n    }\n\n    public static void runTickedStartAndEnd(BooleanSupplier runUntilFalse) {\n        new ServerTickingScheduler(runUntilFalse, EnumSet.allOf(Phase.class));\n    }\n\n    public static void runOnServerOnce(Runnable runnable) {\n        runTickedStartAndEnd(() -> {\n            runnable.run();\n            return false;\n        });\n    }\n\n    private final BooleanSupplier runnable;\n    private final EnumSet<Phase> phases;\n\n    private ServerTickingScheduler(BooleanSupplier runnable, EnumSet<Phase> phases) {\n        this.runnable = runnable;\n        MinecraftForge.EVENT_BUS.register(this);\n        this.phases = phases;\n    }\n\n    @SubscribeEvent\n    public void tick(ServerTickEvent event) {\n        if (! phases.contains(event.phase))\n            return;\n        if (! runnable.getAsBoolean())\n            MinecraftForge.EVENT_BUS.unregister(this);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/concurrent/SteppedScheduler.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.concurrent;\n\nimport java.util.function.BooleanSupplier;\n\npublic abstract class SteppedScheduler implements BooleanSupplier {\n    protected enum StepResult {\n        SUCCESS,\n        FAILURE,\n        END;\n\n        public static StepResult ofBoolean(boolean b) {\n            return b ? SUCCESS : END;\n        }\n    }\n    private final int steps;\n    private boolean finished;\n\n    public SteppedScheduler(int steps) {\n        this.steps = steps;\n        this.finished = false;\n    }\n\n    @Override\n    public boolean getAsBoolean() {\n        if (finished)\n            return false;\n        for (int i = 0; advance() != StepResult.END && i < steps - 1; ++ i)\n            ;\n        boolean res = advance() != StepResult.END;\n        if (! res) {\n            this.finished = true;\n            onFinish();\n        }\n        return res;\n    }\n\n    protected abstract StepResult advance();\n\n    protected abstract void onFinish();\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/concurrent/UndoScheduler.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.concurrent;\n\nimport com.direwolf20.buildinggadgets.common.blocks.EffectBlock;\nimport com.direwolf20.buildinggadgets.common.tainted.building.BlockData;\nimport com.direwolf20.buildinggadgets.common.tainted.building.PlacementTarget;\nimport com.direwolf20.buildinggadgets.common.tainted.building.tilesupport.TileSupport;\nimport com.direwolf20.buildinggadgets.common.tainted.building.view.BuildContext;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.IItemIndex;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.MatchResult;\nimport com.direwolf20.buildinggadgets.common.blocks.OurBlocks;\nimport com.direwolf20.buildinggadgets.common.tainted.save.Undo;\nimport com.direwolf20.buildinggadgets.common.tainted.save.Undo.BlockInfo;\nimport com.direwolf20.buildinggadgets.common.tileentities.ConstructionBlockTileEntity;\nimport com.google.common.base.Preconditions;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.level.block.entity.BlockEntity;\nimport net.minecraft.core.BlockPos;\nimport net.minecraftforge.common.MinecraftForge;\nimport net.minecraftforge.event.level.BlockEvent;\n\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Spliterator;\n\npublic final class UndoScheduler extends SteppedScheduler {\n    public static UndoScheduler scheduleUndo(Undo undo, IItemIndex index, BuildContext context, int steps) {\n        Preconditions.checkArgument(steps > 0);\n\n        UndoScheduler res = new UndoScheduler(\n                Objects.requireNonNull(undo),\n                Objects.requireNonNull(index),\n                Objects.requireNonNull(context),\n                steps\n        );\n\n        ServerTickingScheduler.runTicked(res);\n        return res;\n    }\n\n    private final Spliterator<Map.Entry<BlockPos, BlockInfo>> spliterator;\n    private boolean lastWasSuccess;\n    private final BuildContext context;\n    private final IItemIndex index;\n\n    private UndoScheduler(Undo undo, IItemIndex index, BuildContext context, int steps) {\n        super(steps);\n        assert context.getPlayer() != null;\n        assert ! context.getStack().isEmpty();\n\n        this.spliterator = undo.getUndoData().entrySet().spliterator();\n        this.index = index;\n        this.context = context;\n    }\n\n    @Override\n    protected StepResult advance() {\n        if (! spliterator.tryAdvance(this::undoBlock))\n            return StepResult.END;\n        return lastWasSuccess ? StepResult.SUCCESS : StepResult.FAILURE;\n    }\n\n    private void undoBlock(Map.Entry<BlockPos, BlockInfo> entry) {\n        //if the block that was placed is no longer there, we should not undo anything\n        BlockState state = context.getWorld().getBlockState(entry.getKey());\n        BlockEntity te = context.getWorld().getBlockEntity(entry.getKey());\n        BlockData data;\n        if (state.getBlock() == OurBlocks.CONSTRUCTION_BLOCK.get() && te instanceof ConstructionBlockTileEntity) {\n            data = ((ConstructionBlockTileEntity) te).getConstructionBlockData();\n        } else\n            data = TileSupport.createBlockData(state, te);\n        if (data.getState().getBlock().defaultBlockState() != entry.getValue().getPlacedData().getState().getBlock().defaultBlockState()) {\n            lastWasSuccess = false;\n            return;\n        }\n        if (! state.isAir()) {\n            BlockEvent.BreakEvent event = new BlockEvent.BreakEvent(context.getServerWorld(), entry.getKey(), state, context.getPlayer());\n            if (MinecraftForge.EVENT_BUS.post(event)) {\n                lastWasSuccess = false;\n                return;\n            }\n        }\n        MatchResult matchResult = index.tryMatch(entry.getValue().getProducedItems());\n        lastWasSuccess = matchResult.isSuccess();\n        if (lastWasSuccess) {\n            index.applyMatch(matchResult);\n            index.insert(entry.getValue().getUsedItems());\n            EffectBlock.spawnUndoBlock(context, new PlacementTarget(entry.getKey(), entry.getValue().getRecordedData()));\n        }\n    }\n\n    @Override\n    protected void onFinish() {\n\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/inventory/CreativeItemIndex.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.inventory;\n\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.materials.MaterialList;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.materials.objects.IUniqueObject;\nimport com.google.common.collect.ImmutableMultiset;\nimport com.google.common.collect.Multiset;\n\nimport java.util.Iterator;\n\n/**\n * Represents the Items available in Creative Mode: everything. All queries will succeed. Always.\n */\npublic final class CreativeItemIndex implements IItemIndex {\n    @Override\n    public Multiset<IUniqueObject<?>> insert(Multiset<IUniqueObject<?>> items, boolean simulate) {\n        return items;\n    }\n\n    @Override\n    public void reIndex() {\n\n    }\n\n    @Override\n    public MatchResult tryMatch(MaterialList list) {\n        Iterator<ImmutableMultiset<IUniqueObject<?>>> it = list.iterator();\n        ImmutableMultiset<IUniqueObject<?>> chosen = it.hasNext() ? it.next() : ImmutableMultiset.of();\n        return MatchResult.success(list, chosen, chosen);\n    }\n\n    @Override\n    public boolean applyMatch(MatchResult result) {\n        return result.isSuccess();\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/inventory/HandlerInsertProvider.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.inventory;\n\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraftforge.items.IItemHandler;\n\n/**\n * {@link IInsertProvider} which allows insertion into a single {@link IItemHandler} (for example a remote inventory).\n */\npublic final class HandlerInsertProvider implements IInsertProvider {\n    private final IItemHandler remoteInventory;\n\n    public HandlerInsertProvider(IItemHandler remoteInventory) {\n        this.remoteInventory = remoteInventory;\n    }\n\n    @Override\n    public int insert(ItemStack stack, int count, boolean simulate) {\n        for (int i = 0; i < remoteInventory.getSlots(); i++) {\n            if (stack.isEmpty())\n                return count;\n            stack = remoteInventory.insertItem(count, stack, simulate);\n        }\n        return count - stack.getCount();\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/inventory/IInsertProvider.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.inventory;\n\nimport net.minecraft.world.item.ItemStack;\n\n/**\n * Represents anything that can accept ItemStack insertions. Like for example an {@link net.minecraftforge.items.IItemHandler} or the player's ability to pickup blocks.\n */\npublic interface IInsertProvider {\n    int insert(ItemStack stack, int count, boolean simulate);\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/inventory/IItemIndex.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.inventory;\n\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.materials.MaterialList;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.materials.objects.IUniqueObject;\nimport com.google.common.collect.Multiset;\n\n/**\n * Represents Index for accessible Items. It allows for extraction/insertion into some kind of IUniqueObject container(s).\n * An Implementation must also handle the options represented by a MaterialList correctly - test all available options until the first one matches, or no other is left\n * to search.\n * <p>\n * To update this index with contents that were inserted/extracted independently, you'll need to call {@link #reIndex()}.\n *\n * @see PlayerItemIndex\n * @see CreativeItemIndex\n */\npublic interface IItemIndex {\n    default Multiset<IUniqueObject<?>> insert(Multiset<IUniqueObject<?>> items) {\n        return insert(items, false);\n    }\n\n    //returns the remaining items\n    Multiset<IUniqueObject<?>> insert(Multiset<IUniqueObject<?>> items, boolean simulate);\n\n    void reIndex();\n\n    MatchResult tryMatch(MaterialList list);\n\n    default MatchResult tryMatch(Multiset<IUniqueObject<?>> items) {\n        return tryMatch(MaterialList.of(items));\n    }\n\n    boolean applyMatch(MatchResult result);\n\n    default boolean applyMatch(MaterialList list) {\n        MatchResult result = tryMatch(list);\n        return result.isSuccess() && applyMatch(result);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/inventory/InventoryHelper.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.inventory;\n\nimport com.direwolf20.buildinggadgets.common.blocks.OurBlocks;\nimport com.direwolf20.buildinggadgets.common.items.*;\nimport com.direwolf20.buildinggadgets.common.tainted.building.BlockData;\nimport com.direwolf20.buildinggadgets.common.tainted.building.tilesupport.TileSupport;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.handle.IHandleProvider;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.handle.IObjectHandle;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.handle.ItemHandlerProvider;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.materials.MaterialList;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.materials.objects.UniqueItem;\nimport com.direwolf20.buildinggadgets.common.tainted.registry.TopologicalRegistryBuilder;\nimport com.direwolf20.buildinggadgets.common.tileentities.ConstructionBlockTileEntity;\nimport com.direwolf20.buildinggadgets.common.util.CommonUtils;\nimport com.direwolf20.buildinggadgets.common.util.ref.Reference;\nimport com.google.common.collect.ImmutableList;\nimport com.google.common.collect.ImmutableSet;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.world.InteractionHand;\nimport net.minecraft.world.entity.player.Inventory;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.item.Item;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.context.BlockPlaceContext;\nimport net.minecraft.world.item.context.UseOnContext;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.CropBlock;\nimport net.minecraft.world.level.block.DoorBlock;\nimport net.minecraft.world.level.block.DoublePlantBlock;\nimport net.minecraft.world.level.block.LiquidBlock;\nimport net.minecraft.world.level.block.entity.BlockEntity;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.level.block.state.properties.BlockStateProperties;\nimport net.minecraft.world.level.block.state.properties.DoubleBlockHalf;\nimport net.minecraft.world.level.block.state.properties.Property;\nimport net.minecraftforge.common.capabilities.ForgeCapabilities;\nimport net.minecraftforge.fml.InterModComms;\nimport net.minecraftforge.items.IItemHandler;\nimport net.minecraftforge.items.wrapper.EmptyHandler;\n\nimport java.util.*;\nimport java.util.function.Supplier;\n\n/*\n * @MichaelHillcox\n *  This entire class could do with some refactoring and cleaning :grin:\n */\npublic class InventoryHelper {\n    public static final MaterialList PASTE_LIST = MaterialList.of(new UniqueItem(OurItems.CONSTRUCTION_PASTE_ITEM.get()));\n\n    private static final Set<Property<?>> UNSAFE_PROPERTIES =\n            ImmutableSet.<Property<?>>builder()\n                    .add(BlockStateProperties.SOUTH)\n                    .add(BlockStateProperties.EAST)\n                    .add(BlockStateProperties.WEST)\n                    .add(BlockStateProperties.NORTH)\n                    .add(BlockStateProperties.UP)\n                    .add(BlockStateProperties.DOWN)\n                    .build();\n\n    private static final Set<Property<?>> BASE_UNSAFE_PROPERTIES =\n            ImmutableSet.<Property<?>>builder()\n                    .add(CropBlock.AGE)\n                    .add(DoublePlantBlock.HALF)\n                    .add(BlockStateProperties.WATERLOGGED)\n                    .build();\n\n    public static final CreativeItemIndex CREATIVE_INDEX = new CreativeItemIndex();\n\n    public static IItemIndex index(ItemStack tool, Player player) {\n        if (player.isCreative())\n            return CREATIVE_INDEX;\n        return new PlayerItemIndex(tool, player);\n    }\n\n    static List<IInsertProvider> indexInsertProviders(ItemStack tool, Player player) {\n        ImmutableList.Builder<IInsertProvider> builder = ImmutableList.builder();\n\n        InventoryLinker.getLinkedInventory(player.level, tool).ifPresent(e -> builder.add(new HandlerInsertProvider(e)));\n        builder.add(new PlayerInventoryInsertProvider(player));\n\n        return builder.build();\n    }\n\n    static Map<Class<?>, Map<Object, List<IObjectHandle<?>>>> indexMap(ItemStack tool, Player player) {\n        Map<Class<?>, Map<Object, List<IObjectHandle<?>>>> map = new HashMap<>();\n        for (IItemHandler handler : getHandlers(tool, player)) {\n            if (handler != null && handler.getSlots() > 0)\n                ItemHandlerProvider.index(handler, map);\n        }\n        return map;\n    }\n\n    static List<IItemHandler> getHandlers(ItemStack stack, Player player) {\n        List<IItemHandler> handlers = new ArrayList<>();\n\n        InventoryLinker.getLinkedInventory(player.level, stack).ifPresent(handlers::add);\n        player.getCapability(ForgeCapabilities.ITEM_HANDLER).ifPresent(handlers::add);\n\n        return handlers;\n    }\n\n    public static void registerHandleProviders() {\n        InterModComms.sendTo(Reference.MODID, Reference.HandleProviderReference.IMC_METHOD_HANDLE_PROVIDER, () -> (Supplier<TopologicalRegistryBuilder<IHandleProvider>>)\n                (() -> TopologicalRegistryBuilder.<IHandleProvider>create()\n                        .addMarker(Reference.MARKER_AFTER_RL)\n                        .addValue(Reference.HandleProviderReference.STACK_HANDLER_ITEM_HANDLE_RL, new ItemHandlerProvider())\n                        .addDependency(Reference.HandleProviderReference.STACK_HANDLER_ITEM_HANDLE_RL, Reference.MARKER_AFTER_RL)));\n    }\n\n    public static boolean giveItem(ItemStack itemStack, Player player, Level world) {\n        if (player.isCreative()) {\n            return true;\n        }\n        if (itemStack.getItem() instanceof ConstructionPaste) {\n            itemStack = addPasteToContainer(player, itemStack);\n        }\n        if (itemStack.getCount() == 0) {\n            return true;\n        }\n\n        //Fill any unfilled stacks in the player's inventory first\n        Inventory inv = player.getInventory();\n        List<Integer> slots = findItem(itemStack.getItem(), inv);\n        for (int slot : slots) {\n            ItemStack stackInSlot = inv.getItem(slot);\n            if (stackInSlot.getCount() < stackInSlot.getItem().getMaxStackSize(stackInSlot)) {\n                ItemStack giveItemStack = itemStack.copy();\n                boolean success = inv.add(giveItemStack);\n                if (success) return true;\n            }\n        }\n\n        //Try to insert into the remote inventory.\n        ItemStack tool = AbstractGadget.getGadget(player);\n        IItemHandler remoteInventory = InventoryLinker.getLinkedInventory(world, tool).orElse(new EmptyHandler());\n        if (remoteInventory.getSlots() > 0) {\n            for (int i = 0; i < remoteInventory.getSlots(); i++) {\n                ItemStack containerItem = remoteInventory.getStackInSlot(i);\n                ItemStack giveItemStack = itemStack.copy();\n                if (containerItem.getItem() == itemStack.getItem() || containerItem.isEmpty()) {\n                    giveItemStack = remoteInventory.insertItem(i, giveItemStack, false);\n                    if (giveItemStack.isEmpty())\n                        return true;\n\n                    itemStack = giveItemStack.copy();\n                }\n            }\n        }\n\n\n        List<IItemHandler> invContainers = findInvContainers(inv);\n        if (invContainers.size() > 0) {\n            for (IItemHandler container : invContainers) {\n                for (int i = 0; i < container.getSlots(); i++) {\n                    ItemStack containerItem = container.getStackInSlot(i);\n                    ItemStack giveItemStack = itemStack.copy();\n                    if (containerItem.getItem() == giveItemStack.getItem()) {\n                        giveItemStack = container.insertItem(i, giveItemStack, false);\n                        if (giveItemStack.isEmpty())\n                            return true;\n\n                        itemStack = giveItemStack.copy();\n                    }\n                }\n            }\n        }\n        ItemStack giveItemStack = itemStack.copy();\n        return inv.add(giveItemStack);\n    }\n\n    public static ItemStack addPasteToContainer(Player player, ItemStack itemStack) {\n        if (!(itemStack.getItem() instanceof ConstructionPaste)) {\n            return itemStack;\n        }\n\n        Inventory inv = player.getInventory();\n        List<Integer> slots = findItemClass(ConstructionPasteContainer.class, inv);\n        if (slots.size() == 0) {\n            return itemStack;\n        }\n\n        Map<Integer, Integer> slotMap = new HashMap<>();\n        for (int slot : slots) {\n            slotMap.put(slot, ConstructionPasteContainer.getPasteAmount(inv.getItem(slot)));\n        }\n\n        List<Map.Entry<Integer, Integer>> list = new ArrayList<>(slotMap.entrySet());\n        Comparator<Map.Entry<Integer, Integer>> comparator = Map.Entry.comparingByValue();\n        comparator = comparator.reversed();\n        list.sort(comparator);\n\n\n        for (Map.Entry<Integer, Integer> entry : list) {\n            ItemStack containerStack = inv.getItem(entry.getKey());\n            ConstructionPasteContainer item = ((ConstructionPasteContainer) containerStack.getItem());\n\n            int maxAmount = item.getMaxCapacity();\n            int pasteInContainer = ConstructionPasteContainer.getPasteAmount(containerStack);\n            int freeSpace = item.isCreative() ? Integer.MAX_VALUE : maxAmount - pasteInContainer;\n            int stackSize = itemStack.getCount();\n\n            int remainingPaste = stackSize - freeSpace;\n            if (remainingPaste < 0) {\n                remainingPaste = 0;\n            }\n\n            int usedPaste = Math.abs(stackSize - remainingPaste);\n            itemStack.setCount(remainingPaste);\n            ConstructionPasteContainer.setPasteAmount(containerStack, pasteInContainer + usedPaste);\n        }\n\n        return itemStack;\n    }\n\n    private static List<IItemHandler> findInvContainers(Inventory inv) {\n        List<IItemHandler> containers = new ArrayList<>();\n\n        for (int i = 0; i < 36; ++i) {\n            ItemStack stack = inv.getItem(i);\n            stack.getCapability(ForgeCapabilities.ITEM_HANDLER)\n                    .ifPresent(containers::add);\n        }\n\n        return containers;\n    }\n\n    public static int countInContainer(IItemHandler container, Item item) {\n        int count = 0;\n        ItemStack tempItem;\n        for (int i = 0; i < container.getSlots(); ++i) {\n            tempItem = container.getStackInSlot(i);\n            if (tempItem.getItem() == item) {\n                count += tempItem.getCount();\n            }\n        }\n        return count;\n    }\n\n    private static List<Integer> findItem(Item item, Inventory inv) {\n        List<Integer> slots = new ArrayList<>();\n        for (int i = 0; i < 36; ++i) {\n            ItemStack stack = inv.getItem(i);\n            if (!stack.isEmpty() && stack.getItem() == item) {\n                slots.add(i);\n            }\n        }\n        return slots;\n    }\n\n    public static List<Integer> findItemClass(Class<?> c, Inventory inv) {\n        List<Integer> slots = new ArrayList<>();\n        for (int i = 0; i < 36; ++i) {\n            ItemStack stack = inv.getItem(i);\n            if (!stack.isEmpty() && c.isInstance(stack.getItem())) {\n                slots.add(i);\n            }\n        }\n        return slots;\n    }\n\n    public static ItemStack getSilkTouchDrop(BlockState state) {\n        return new ItemStack(state.getBlock());\n    }\n\n    public static Optional<BlockData> getSafeBlockData(Player player, BlockPos pos, InteractionHand hand) {\n        BlockPlaceContext blockItemUseContext = new BlockPlaceContext(new UseOnContext(player, hand, CommonUtils.fakeRayTrace(player.position(), pos)));\n        return getSafeBlockData(player, pos, blockItemUseContext);\n    }\n\n    public static Optional<BlockData> getSafeBlockData(Player player, BlockPos pos, BlockPlaceContext useContext) {\n        Level world = player.level;\n        Boolean isCopyPasteGadget = (AbstractGadget.getGadget(player).getItem() instanceof GadgetCopyPaste);\n        BlockState state = world.getBlockState(pos);\n        if (state.getBlock() instanceof LiquidBlock)\n            return Optional.empty();\n\n        if (state.getBlock() == OurBlocks.CONSTRUCTION_BLOCK.get()) {\n            BlockEntity te = world.getBlockEntity(pos);\n            if (te instanceof ConstructionBlockTileEntity) //should already be checked\n                return Optional.of(((ConstructionBlockTileEntity) te).getConstructionBlockData());\n        }\n\n        // Support doors\n        if (state.getBlock() instanceof DoorBlock && state.getValue(BlockStateProperties.DOUBLE_BLOCK_HALF) == DoubleBlockHalf.UPPER) {\n            return Optional.empty();\n        }\n\n        BlockState placeState = state.getBlock().defaultBlockState();\n        for (Property<?> prop : placeState.getProperties()) {\n            if (BASE_UNSAFE_PROPERTIES.contains(prop) || !isCopyPasteGadget && UNSAFE_PROPERTIES.contains(prop)) {\n                continue;\n            }\n            placeState = applyProperty(placeState, state, prop);\n        }\n\n        return Optional.of(new BlockData(placeState, TileSupport.createTileData(world, pos)));\n    }\n\n    //proper generics...\n    private static <T extends Comparable<T>> BlockState applyProperty(BlockState state, BlockState from, Property<T> prop) {\n        return state.setValue(prop, from.getValue(prop));\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/inventory/InventoryLinker.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.inventory;\n\nimport com.direwolf20.buildinggadgets.common.util.lang.MessageTranslation;\nimport com.direwolf20.buildinggadgets.common.util.ref.NBTKeys;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.registries.Registries;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.nbt.NbtUtils;\nimport net.minecraft.resources.ResourceKey;\nimport net.minecraft.resources.ResourceLocation;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.entity.BlockEntity;\nimport net.minecraft.world.phys.BlockHitResult;\nimport net.minecraftforge.common.capabilities.ForgeCapabilities;\nimport net.minecraftforge.common.util.LazyOptional;\nimport net.minecraftforge.items.IItemHandler;\nimport org.apache.commons.lang3.tuple.Pair;\n\nimport javax.annotation.Nullable;\n\npublic class InventoryLinker {\n    /**\n     * Perform the link to the inventory\n     */\n    public static Result linkInventory(Level world, ItemStack stack, BlockHitResult trace) {\n        BlockEntity tileEntity = world.getBlockEntity(trace.getBlockPos());\n        if (tileEntity == null || !tileEntity.getCapability(ForgeCapabilities.ITEM_HANDLER).isPresent()) {\n            return Result.fail(MessageTranslation.INVALID_BOUND_TILE);\n        }\n\n        // remove if the existing linked inventory is the same block we're setting now.\n        boolean removed = getLinkedInventory(world, stack)\n                .map(e -> removeIfSame(stack, trace.getBlockPos()))\n                .orElse(false);\n\n        if (removed) {\n            return Result.removed();\n        }\n\n        // Set the relevant data\n        CompoundTag compound = stack.getOrCreateTag();\n        compound.putString(NBTKeys.REMOTE_INVENTORY_DIM, world.dimension().location().toString());\n        compound.put(NBTKeys.REMOTE_INVENTORY_POS, NbtUtils.writeBlockPos(trace.getBlockPos()));\n        return Result.success();\n    }\n\n    /**\n     * Directly fetch the linked inventory if the tile exists (removes if not) and if the tile holds\n     * a capability.\n     */\n    public static LazyOptional<IItemHandler> getLinkedInventory(Level world, BlockPos pos, ResourceKey<Level> registry, @Nullable ItemStack stack) {\n        if (!world.dimension().equals(registry)) {\n            return LazyOptional.empty();\n        }\n\n        BlockEntity tileEntity = world.getBlockEntity(pos);\n        if (tileEntity == null) {\n            // Unlink if the tile entity no longer exists\n            if (stack != null) {\n                removeDataFromStack(stack);\n            }\n\n            return LazyOptional.empty();\n        }\n\n        return tileEntity.getCapability(ForgeCapabilities.ITEM_HANDLER);\n    }\n\n    public static LazyOptional<IItemHandler> getLinkedInventory(Level world, ItemStack stack) {\n        Pair<BlockPos, ResourceKey<Level>> dataFromStack = getDataFromStack(stack);\n        if (dataFromStack == null) {\n            return LazyOptional.empty();\n        }\n\n        return getLinkedInventory(world, dataFromStack.getKey(), dataFromStack.getValue(), stack);\n    }\n\n    /**\n     * Remove the link from the ItemStack if the pos is the same as the target pos. This creates a toggle effect.\n     *\n     * @implNote Ideally this would not have to get the same data twice but for now, this works fine.\n     */\n    private static boolean removeIfSame(ItemStack stack, BlockPos pos) {\n        // This isn't ideal that we have to do this twice\n        Pair<BlockPos, ResourceKey<Level>> dataFromStack = getDataFromStack(stack);\n        if (dataFromStack == null) {\n            return false;\n        }\n\n        if (dataFromStack.getKey().equals(pos)) {\n            removeDataFromStack(stack);\n            return true;\n        }\n\n        return false;\n    }\n\n    /**\n     * Removes the keys from the stack to allow for lazy contains\n     */\n    public static void removeDataFromStack(ItemStack stack) {\n        CompoundTag compound = stack.getOrCreateTag();\n        compound.remove(NBTKeys.REMOTE_INVENTORY_POS);\n        compound.remove(NBTKeys.REMOTE_INVENTORY_DIM);\n    }\n\n    /**\n     * Retrieves the link data from the ItemStack\n     */\n    @Nullable\n    public static Pair<BlockPos, ResourceKey<Level>> getDataFromStack(ItemStack stack) {\n        CompoundTag compound = stack.getOrCreateTag();\n        if (!compound.contains(NBTKeys.REMOTE_INVENTORY_POS) || !compound.contains(NBTKeys.REMOTE_INVENTORY_DIM)) {\n            return null;\n        }\n\n        ResourceKey<Level> dimKey = ResourceKey.create(Registries.DIMENSION, new ResourceLocation(compound.getString(NBTKeys.REMOTE_INVENTORY_DIM)));\n        return Pair.of(\n            NbtUtils.readBlockPos(compound.getCompound(NBTKeys.REMOTE_INVENTORY_POS)),\n            dimKey\n        );\n    }\n\n    /**\n     * Handles if the Link was successful and a message to go with it.\n     */\n    public final static class Result {\n        private final MessageTranslation i18n;\n        private final boolean successful;\n\n        public Result(MessageTranslation i18n, boolean successful) {\n            this.i18n = i18n;\n            this.successful = successful;\n        }\n\n        public static Result fail(MessageTranslation i18n) {\n            return new Result(i18n, false);\n        }\n\n        public static Result success() {\n            return new Result(MessageTranslation.BOUND_TO_TILE, true);\n        }\n\n        public static Result removed() {\n            return new Result(MessageTranslation.UNBOUND_TO_TILE, true);\n        }\n\n        public MessageTranslation getI18n() {\n            return i18n;\n        }\n\n        public boolean isSuccessful() {\n            return successful;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/inventory/MatchResult.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.inventory;\n\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.materials.MaterialList;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.materials.objects.IUniqueObject;\nimport com.google.common.collect.ImmutableMultiset;\n\n/**\n * The result of a match by an {@link IItemIndex}. Allows access to the {@link #getMatchedList() matched MaterialList}, the Items which were found, the chosen option and of course\n * whether the match was a success or not.\n */\npublic final class MatchResult {\n    private final MaterialList matchedList;\n    private final ImmutableMultiset<IUniqueObject<?>> foundItems;\n    private final ImmutableMultiset<IUniqueObject<?>> chosenOption;\n    private final boolean isSuccess;\n\n    public static MatchResult success(MaterialList matchedList, ImmutableMultiset<IUniqueObject<?>> foundItems, ImmutableMultiset<IUniqueObject<?>> chosenOption) {\n        return new MatchResult(matchedList, foundItems, chosenOption, true);\n    }\n\n    public static MatchResult failure() {\n        return new MatchResult(MaterialList.empty(), ImmutableMultiset.of(), ImmutableMultiset.of(), false);\n    }\n\n    public static MatchResult failure(MaterialList matchedList, ImmutableMultiset<IUniqueObject<?>> foundItems, ImmutableMultiset<IUniqueObject<?>> chosenOption) {\n        return new MatchResult(matchedList, foundItems, chosenOption, false);\n    }\n\n    MatchResult(MaterialList matchedList, ImmutableMultiset<IUniqueObject<?>> foundItems, ImmutableMultiset<IUniqueObject<?>> chosenOption, boolean isSuccess) {\n        this.matchedList = matchedList;\n        this.foundItems = foundItems;\n        this.chosenOption = chosenOption;\n        this.isSuccess = isSuccess;\n    }\n\n    public MaterialList getMatchedList() {\n        return matchedList;\n    }\n\n    /**\n     * If this result is a success, then this will be a reference to the same set returned by {@link #getChosenOption()} as all the {@link IUniqueObject unique objects}\n     * in there will be available. If this match is not a success, then this will return the amount of found Items for all {@link IUniqueObject unique objects} across\n     * all options.\n     *\n     * @return The found items\n     */\n    public ImmutableMultiset<IUniqueObject<?>> getFoundItems() {\n        return foundItems;\n    }\n\n    public ImmutableMultiset<IUniqueObject<?>> getChosenOption() {\n        return chosenOption;\n    }\n\n    public boolean isSuccess() {\n        return isSuccess;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/inventory/PlayerInventoryInsertProvider.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.inventory;\n\nimport net.minecraft.world.entity.item.ItemEntity;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraftforge.event.ForgeEventFactory;\n\n/**\n * An {@link net.minecraftforge.items.IItemHandler} which inserts items into the PlayerInventory by making it pick up a freshly created ItemStack.\n */\npublic final class PlayerInventoryInsertProvider implements IInsertProvider {\n    private final Player player;\n\n    public PlayerInventoryInsertProvider(Player player) {\n        this.player = player;\n    }\n\n    @Override\n    public int insert(ItemStack stack, int count, boolean simulate) {\n        ItemStack copy = stack.copy();\n        if (copy.getCount() != count)\n            copy.setCount(count);\n\n        int eventResponse = ForgeEventFactory.onItemPickup(new ItemEntity(player.level, player.getX(), player.getY(), player.getZ(), copy), player);\n        // 0  = picking up was not handled by anyone else, we'll add to inventory what we can.\n        if( eventResponse == 0 ) {\n            player.getInventory().add(copy);\n            if (copy.isEmpty()) {\n                return count;\n            } else {\n                return count - copy.getCount();\n            }\n        }\n        // eventResponse = -1 or 1, it was handled by someone else, we'll have to assume they handled it all.\n        return count;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/inventory/PlayerItemIndex.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.inventory;\n\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.handle.IObjectHandle;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.materials.MaterialList;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.materials.objects.IUniqueObject;\nimport com.google.common.collect.*;\nimport com.google.common.collect.Multiset.Entry;\nimport net.minecraft.world.entity.item.ItemEntity;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.item.Item;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.Items;\n\nimport java.util.*;\n\n/**\n * Item Index representation all Items accessible for the Player by BuildingGadgets.\n * To allow for better performance, the Items in the player's Inventory are indexed by their Item and upon query only those with the appropriate Item need to be iterated.\n */\npublic final class PlayerItemIndex implements IItemIndex {\n    //use a class map first, to allow for non-Item IUniqueObjects...\n    private Map<Class<?>, Map<Object, List<IObjectHandle<?>>>> handleMap;\n    private List<IInsertProvider> insertProviders;\n    private final ItemStack stack;\n    private final Player player;\n\n    public PlayerItemIndex(ItemStack stack, Player player) {\n        this.stack = stack;\n        this.player = player;\n        reIndex();\n    }\n\n    @Override\n    public Multiset<IUniqueObject<?>> insert(Multiset<IUniqueObject<?>> items, boolean simulate) {\n        Multiset<IUniqueObject<?>> copy = HashMultiset.create(items);\n        Multiset<IUniqueObject<?>> toRemove = HashMultiset.create();\n        for (Multiset.Entry<IUniqueObject<?>> entry : copy.entrySet()) {\n            int remainingCount = insertObject(entry.getElement(), entry.getCount(), simulate);\n            if (remainingCount < entry.getCount())\n                toRemove.add(entry.getElement(), entry.getCount() - remainingCount);\n        }\n        Multisets.removeOccurrences(copy, toRemove);\n\n        return copy;\n    }\n\n    private int insertObject(IUniqueObject<?> obj, int count, boolean simulate) {\n        if (obj.preferStackInsert())\n            return obj.tryCreateInsertStack(Collections.unmodifiableMap(handleMap), count)\n                    .map(itemStack -> performSimpleInsert(itemStack, count, simulate))\n                    .orElseGet(() -> performComplexInsert(obj, count, simulate));\n        else {\n            // This likely has the same disregard for valid slots as the simple insert does\n            int remainingCount = performComplexInsert(obj, count, simulate);\n            return remainingCount == 0 ? 0 : obj.tryCreateInsertStack(Collections.unmodifiableMap(handleMap), count)\n                    .map(itemStack -> performSimpleInsert(itemStack, count, simulate))\n                    .orElse(remainingCount);\n        }\n    }\n\n    private int performSimpleInsert(ItemStack stack, int count, boolean simulate) {\n        int remainingCount = insertIntoProviders(stack, count, simulate);\n        if (remainingCount == 0)\n            return 0;\n\n// this is extremely buggy and poorly planned out code.\n//        insertIntoEmptyHandles(stack, remainingCount, simulate);\n//        if (remainingCount == 0)\n//            return 0;\n\n        if (! simulate)\n            spawnRemainder(stack, remainingCount);\n\n        return 0;\n    }\n\n    private int insertIntoProviders(ItemStack stack, int remainingCount, boolean simulate) {\n        for (IInsertProvider insertProvider : insertProviders) {\n            remainingCount -= insertProvider.insert(stack, remainingCount, simulate);\n            if (remainingCount <= 0)\n                return 0;\n        }\n        return remainingCount;\n    }\n\n    // todo: fix or rewrite. has many root issues:\n    //       uses methods not intended for forge, has the ability to replace stacks, indexes players inventory even though we already handle the players inventory,\n    //       doesn't check for a valid slot, ignores the IItemHandler contract. Maybe more\n    private int insertIntoEmptyHandles(ItemStack stack, int remainingCount, boolean simulate) {\n//        List<IObjectHandle<?>> emptyHandles = handleMap\n//                .computeIfAbsent(Item.class, c -> new HashMap<>())\n//                .getOrDefault(Items.AIR, ImmutableList.of());\n//\n//        for (Iterator<IObjectHandle<?>> it = emptyHandles.iterator(); it.hasNext() && remainingCount >= 0; ) {\n//            IObjectHandle<?> handle = it.next();\n//            UniqueItem item = UniqueItem.ofStack(stack);\n//\n//            int match = handle.insert(item, remainingCount, simulate);\n//            if (match > 0)\n//                remainingCount -= match;\n//\n//            handleMap.get(Item.class)\n//                    .computeIfAbsent(item.getIndexObject(), i -> new ArrayList<>())\n//                    .add(handle);\n//\n//            if (remainingCount <= 0)\n//                return 0;\n//        }\n//\n//        return remainingCount;\n        return 0;\n    }\n\n    private void spawnRemainder(ItemStack stack, int remainingCount) {\n        while (remainingCount > 0) {\n            ItemStack copy = stack.copy();\n            copy.setCount(Math.min(remainingCount, copy.getMaxStackSize()));\n            remainingCount -= copy.getCount();\n            ItemEntity itemEntity = new ItemEntity(player.level, player.getX(), player.getY(), player.getZ(), copy);\n            player.level.addFreshEntity(itemEntity);\n        }\n    }\n\n    private int performComplexInsert(IUniqueObject<?> obj, int count, boolean simulate) {\n        int remainingCount = count;\n        List<IObjectHandle<?>> handles = handleMap\n                .getOrDefault(obj.getIndexClass(), ImmutableMap.of())\n                .getOrDefault(obj.getIndexObject(), ImmutableList.of());\n\n        for (Iterator<IObjectHandle<?>> it = handles.iterator(); it.hasNext() && remainingCount >= 0; ) {\n            IObjectHandle<?> handle = it.next();\n            int match = handle.insert(obj, remainingCount, simulate);\n            if (match > 0)\n                remainingCount -= match;\n            if (handle.shouldCleanup())\n                it.remove();\n            if (remainingCount <= 0)\n                return 0;\n        }\n        return remainingCount;\n    }\n\n    @Override\n    public void reIndex() {\n        this.handleMap = InventoryHelper.indexMap(stack, player);\n        this.insertProviders = InventoryHelper.indexInsertProviders(stack, player);\n    }\n\n    @Override\n    public MatchResult tryMatch(MaterialList list) {\n        MatchResult result = null;\n        for (ImmutableMultiset<IUniqueObject<?>> multiset : list) {\n            result = match(list, multiset, true);\n            if (result.isSuccess())\n                return MatchResult.success(list, result.getFoundItems(), multiset);\n        }\n        return result == null ? MatchResult.success(list, ImmutableMultiset.of(), ImmutableMultiset.of()) : evaluateFailingOptionFoundItems(list);\n    }\n\n    private MatchResult evaluateFailingOptionFoundItems(MaterialList list) {\n        Multiset<IUniqueObject<?>> multiset = HashMultiset.create();\n        for (ImmutableMultiset<IUniqueObject<?>> option : list.getItemOptions()) {\n            for (Entry<IUniqueObject<?>> entry : option.entrySet()) {\n                multiset.setCount(entry.getElement(), Math.max(multiset.count(entry.getElement()), entry.getCount()));\n            }\n        }\n        multiset.addAll(list.getRequiredItems());\n        MatchResult result = match(list, multiset, true);\n        if (result.isSuccess())\n            throw new RuntimeException(\"This should not be possible! The the content changed between matches?!?\");\n        Iterator<ImmutableMultiset<IUniqueObject<?>>> it = list.iterator();\n        return it.hasNext() ? MatchResult.failure(list, result.getFoundItems(), it.next()) : result;\n    }\n\n    private MatchResult match(MaterialList list, Multiset<IUniqueObject<?>> multiset, boolean simulate) {\n        ImmutableMultiset.Builder<IUniqueObject<?>> availableBuilder = ImmutableMultiset.builder();\n        boolean failure = false;\n        for (Entry<IUniqueObject<?>> entry : multiset.entrySet()) {\n            int remainingCount = entry.getCount();\n            Class<?> indexClass = entry.getElement().getIndexClass();\n            List<IObjectHandle<?>> entries = handleMap\n                    .getOrDefault(indexClass, ImmutableMap.of())\n                    .getOrDefault(entry.getElement().getIndexObject(), ImmutableList.of());\n            for (Iterator<IObjectHandle<?>> it = entries.iterator(); it.hasNext() && remainingCount >= 0; ) {\n                IObjectHandle<?> handle = it.next();\n                int match = handle.match(entry.getElement(), remainingCount, simulate);\n                if (match > 0)\n                    remainingCount -= match;\n                if (handle.shouldCleanup()) {\n                    it.remove();\n                    if (indexClass == Item.class)  //make it ready for insertion if this is an Item handle\n                        handleMap.computeIfAbsent(Item.class, c -> new HashMap<>())\n                                .computeIfAbsent(Items.AIR, i -> new ArrayList<>())\n                                .add(handle);\n                }\n            }\n            remainingCount = Math.max(0, remainingCount);\n            if (remainingCount > 0)\n                failure = true;\n            availableBuilder.addCopies(entry.getElement(), entry.getCount() - remainingCount);\n        }\n        if (failure)\n            return MatchResult.failure(list, availableBuilder.build(), ImmutableMultiset.of());\n        return MatchResult.success(list, availableBuilder.build(), ImmutableMultiset.of());\n    }\n\n    @Override\n    public boolean applyMatch(MatchResult result) {\n        if (! result.isSuccess())\n            return false;\n        return match(result.getMatchedList(), result.getChosenOption(), false).isSuccess();\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/inventory/RecordingItemIndex.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.inventory;\n\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.materials.MaterialList;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.materials.objects.IUniqueObject;\nimport com.google.common.collect.HashMultiset;\nimport com.google.common.collect.ImmutableMultiset;\nimport com.google.common.collect.Multiset;\nimport com.google.common.collect.Multisets;\n\n/**\n * An {@link IItemIndex} which instead of inserting or extracting Items from the backing {@link IItemIndex} keeps record of\n * everything that was attempted to be inserted and then simulates extraction/insertion of the combination of the \"record\" and\n * the new Items. Works just like {@link com.direwolf20.buildinggadgets.common.util.tools.SimulateEnergyStorage} - only for Items.\n *\n * @see com.direwolf20.buildinggadgets.common.util.tools.SimulateEnergyStorage\n */\npublic final class RecordingItemIndex implements IItemIndex {\n    private final IItemIndex other;\n    private Multiset<IUniqueObject<?>> extractedItems;\n    private Multiset<IUniqueObject<?>> insertedItems;\n\n    public RecordingItemIndex(IItemIndex other) {\n        this.other = other;\n        this.extractedItems = HashMultiset.create();\n        this.insertedItems = HashMultiset.create();\n    }\n\n    @Override\n    public Multiset<IUniqueObject<?>> insert(Multiset<IUniqueObject<?>> items, boolean simulate) {\n        Multiset<IUniqueObject<?>> res = other.insert(items, simulate);\n        if (! simulate)\n            insertedItems.addAll(items);\n        return res;\n    }\n\n    @Override\n    public void reIndex() {\n        other.reIndex();\n        insertedItems.clear();\n        extractedItems.clear();\n    }\n\n    @Override\n    public MatchResult tryMatch(MaterialList list) {\n        return other.tryMatch(MaterialList.and(list, MaterialList.of(extractedItems)));\n    }\n\n    @Override\n    public MatchResult tryMatch(Multiset<IUniqueObject<?>> items) {\n        return other.tryMatch(ImmutableMultiset.<IUniqueObject<?>>builder()\n                .addAll(items)\n                .addAll(extractedItems)\n                .build());\n    }\n\n    @Override\n    public boolean applyMatch(MatchResult result) {\n        if (result.isSuccess()) {\n            extractedItems.addAll(Multisets.difference(result.getChosenOption(), extractedItems));\n            return true;\n        }\n        return false;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/inventory/handle/IHandleProvider.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.inventory.handle;\n\nimport net.minecraftforge.common.capabilities.ICapabilityProvider;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\npublic interface IHandleProvider {\n    /**\n     * @param capProvider    The capProvider to index\n     * @param indexMap       The index to add to\n     * @param indexedClasses The Set of indexed classes. An {@code IHandleProvider} should add all classes that were indexed by itself to this Set and\n     *                       should also query it, whether any indexing should be performed.\n     */\n    boolean index(ICapabilityProvider capProvider, Map<Class<?>, Map<Object, List<IObjectHandle<?>>>> indexMap, Set<Class<?>> indexedClasses);\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/inventory/handle/IObjectHandle.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.inventory.handle;\n\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.materials.objects.IUniqueObject;\n\npublic interface IObjectHandle<T> {\n    Class<T> getIndexClass();\n\n    T getIndexObject();\n\n    int match(IUniqueObject<?> item, int count, boolean simulate);\n\n    int insert(IUniqueObject<?> item, int count, boolean simulate);\n\n    boolean shouldCleanup();\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/inventory/handle/ItemHandlerProvider.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.inventory.handle;\n\nimport com.direwolf20.buildinggadgets.common.tainted.registry.Registries;\nimport net.minecraft.world.item.Item;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.Items;\nimport net.minecraftforge.common.capabilities.ForgeCapabilities;\nimport net.minecraftforge.common.capabilities.ICapabilityProvider;\nimport net.minecraftforge.common.util.LazyOptional;\nimport net.minecraftforge.items.IItemHandler;\n\nimport java.util.*;\n\n/**\n * Indexes an IITemHandler for {@link IObjectHandle}'s, to allow {@link com.direwolf20.buildinggadgets.common.tainted.inventory.PlayerItemIndex} access\n * to an indexed map of available Items.\n */\npublic final class ItemHandlerProvider implements IHandleProvider {\n    public static void index(IItemHandler handler, Map<Class<?>, Map<Object, List<IObjectHandle<?>>>> indexMap) {\n        List<ItemStack> stacks = new ArrayList<>(handler.getSlots());\n        //first index all sub-providers\n        for (int i = 0; i < handler.getSlots(); ++ i) {\n            ItemStack stack = handler.getStackInSlot(i);\n            stacks.add(stack); //temporarily store this, as this might be an expensive operation (AE-Systems!)\n            if (! stack.isEmpty())\n                Registries.HandleProvider.indexCapProvider(stack, indexMap);\n        }\n        //now append the stacks within this handle\n        for (int i = 0; i < handler.getSlots(); ++ i) {\n            ItemStack stack = stacks.get(i);\n            if (! stack.isEmpty()) {\n                indexMap.computeIfAbsent(Item.class, clazz -> new HashMap<>())\n                        .computeIfAbsent(stack.getItem(), item -> new ArrayList<>())\n                        .add(new StackHandlerItemHandle(handler, i));\n            } else {\n                indexMap.computeIfAbsent(Item.class, clazz -> new HashMap<>())\n                        .computeIfAbsent(Items.AIR, item -> new ArrayList<>())\n                        .add(new StackHandlerItemHandle(handler, i));\n            }\n        }\n    }\n\n    @Override\n    public boolean index(ICapabilityProvider capProvider, Map<Class<?>, Map<Object, List<IObjectHandle<?>>>> indexMap, Set<Class<?>> indexedClasses) {\n        if (indexedClasses.contains(Item.class))\n            return false;\n        LazyOptional<IItemHandler> cap = capProvider.getCapability(ForgeCapabilities.ITEM_HANDLER);\n        if (! cap.isPresent())\n            return false;\n        IItemHandler handler = cap.orElseThrow(RuntimeException::new);\n        index(handler, indexMap);\n        indexedClasses.add(Item.class);\n        return true;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/inventory/handle/StackHandlerItemHandle.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.inventory.handle;\n\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.materials.objects.IUniqueObject;\nimport net.minecraft.world.item.Item;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraftforge.items.IItemHandler;\nimport net.minecraftforge.items.IItemHandlerModifiable;\n\npublic final class StackHandlerItemHandle implements IObjectHandle<Item> {\n    private final IItemHandler handler;\n    private final int slot;\n\n    public StackHandlerItemHandle(IItemHandler handler, int slot) {\n        this.handler = handler;\n        this.slot = slot;\n    }\n\n    @Override\n    public Class<Item> getIndexClass() {\n        return Item.class;\n    }\n\n    @Override\n    public boolean shouldCleanup() {\n        return getStack().isEmpty();\n    }\n\n    @Override\n    public int match(IUniqueObject<?> item, int count, boolean simulate) {\n        ItemStack stack = getStack();\n        if (item.matches(stack)) {\n            ItemStack resultStack = handler.extractItem(slot, count, simulate);\n            return resultStack.getCount();\n        }\n        return 0;\n    }\n\n    @Override\n    public Item getIndexObject() {\n        return getStack().getItem();\n    }\n\n    @Override\n    public int insert(IUniqueObject<?> item, int count, boolean simulate) {\n        if (handler instanceof IItemHandlerModifiable) {\n            IItemHandlerModifiable modifiable = (IItemHandlerModifiable) handler;\n            ItemStack stack = getStack();\n            if (! stack.isEmpty() && ! item.matches(stack))\n                return 0;\n            ItemStack res = item.insertInto(stack, count);\n            if (! simulate)\n                modifiable.setStackInSlot(slot, res);\n            return res.getCount() - stack.getCount();\n        }\n        return 0;\n    }\n\n    private ItemStack getStack() {\n        return handler.getStackInSlot(slot);\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) return true;\n        if (! (o instanceof StackHandlerItemHandle)) return false;\n\n        StackHandlerItemHandle that = (StackHandlerItemHandle) o;\n\n        if (slot != that.slot) return false;\n        return handler.equals(that.handler);\n    }\n\n    @Override\n    public int hashCode() {\n        int result = handler.hashCode();\n        result = 31 * result + slot;\n        return result;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/inventory/materials/AndMaterialListEntry.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.inventory.materials;\n\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.materials.objects.IUniqueObject;\nimport com.direwolf20.buildinggadgets.common.util.ref.NBTKeys;\nimport com.google.common.collect.*;\n\nimport java.util.*;\nimport java.util.stream.Collectors;\n\n/**\n * A {@link SubMaterialListEntry} using \"and\" as the connection between entries.\n */\nclass AndMaterialListEntry extends SubMaterialListEntry {\n    static final MaterialListEntry.Serializer<SubMaterialListEntry> SERIALIZER = new SubMaterialListEntry.Serializer(NBTKeys.AND_SERIALIZER_ID) {\n        @Override\n        protected SubMaterialListEntry create(ImmutableList<MaterialListEntry<?>> subEntries, ImmutableList<SimpleMaterialListEntry> constantEntries) {\n            return new AndMaterialListEntry(subEntries, constantEntries);\n        }\n    };\n\n    AndMaterialListEntry(ImmutableList<MaterialListEntry<?>> subEntries, ImmutableList<SimpleMaterialListEntry> simpleEntries, boolean simplified) {\n        super(subEntries, simpleEntries, simplified);\n    }\n\n    AndMaterialListEntry(ImmutableList<MaterialListEntry<?>> subEntries, ImmutableList<SimpleMaterialListEntry> simpleEntries) {\n        super(subEntries, simpleEntries);\n    }\n\n    AndMaterialListEntry(ImmutableList<MaterialListEntry<?>> subEntries) {\n        super(subEntries);\n    }\n\n    /**\n     * Applies an \"and\" to the contained {@link MaterialListEntry MaterialListEntries}. This happens by slowly incrementing through all options of\n     * the contained entries.\n     */\n    @Override\n    public PeekingIterator<ImmutableMultiset<IUniqueObject<?>>> iterator() {\n        if (! getAllSubEntries().findFirst().isPresent())\n            return Iterators.peekingIterator(Iterators.singletonIterator(ImmutableMultiset.of()));\n        LinkedList<MaterialEntryWrapper> list = getAllSubEntries()\n                .map(MaterialEntryWrapper::new)\n                .collect(Collectors.toCollection(LinkedList::new));\n        return Iterators.peekingIterator(new AbstractIterator<ImmutableMultiset<IUniqueObject<?>>>() {\n            private Deque<MaterialEntryWrapper> dequeue = list;\n\n            @Override\n            protected ImmutableMultiset<IUniqueObject<?>> computeNext() {\n                if (dequeue.isEmpty())\n                    return endOfData();\n                ImmutableMultiset.Builder<IUniqueObject<?>> builder = ImmutableMultiset.builder();\n                for (MaterialEntryWrapper wrapper : dequeue) {\n                    if (wrapper.hasNext())\n                        builder.addAll(wrapper.peek());\n                }\n                Iterator<MaterialEntryWrapper> revIt = dequeue.descendingIterator();\n                MaterialEntryWrapper lastReset = null;\n                while (revIt != null && revIt.hasNext()) {\n                    MaterialEntryWrapper wrapper = revIt.next();\n                    if (wrapper.hasNext()) {\n                        wrapper.advance();\n                        revIt = null;\n                    } else {\n                        wrapper.reset();\n                        lastReset = wrapper;\n                    }\n                }\n                if (lastReset == dequeue.getFirst()) {\n                    dequeue.clear();\n                }\n                return builder.build();\n            }\n        });\n    }\n\n    @Override\n    public MaterialListEntry.Serializer<SubMaterialListEntry> getSerializer() {\n        return SERIALIZER;\n    }\n\n    @Override\n    protected List<MaterialListEntry<?>> orderAndSimplifyEntries(List<OrMaterialListEntry> orEntries,\n                                                                 List<AndMaterialListEntry> andEntries,\n                                                                 List<SimpleMaterialListEntry> simpleEntries) {\n        List<MaterialListEntry<?>> remainder = new ArrayList<>();\n        getSubEntries().forEach(entry -> {\n            MaterialListEntry<?> simplified = entry.simplify();\n            if (simplified instanceof AndMaterialListEntry) {\n                simpleEntries.addAll(((SubMaterialListEntry) simplified).getConstantEntries());\n                //There's no need for nested and's\n                pullUpInnerEntries((AndMaterialListEntry) entry, orEntries, andEntries, simpleEntries, remainder);\n            } else if (simplified instanceof OrMaterialListEntry) {\n                simpleEntries.addAll(((SubMaterialListEntry) simplified).getConstantEntries());\n                orEntries.add(new OrMaterialListEntry(((SubMaterialListEntry) simplified).getSubEntries(), ImmutableList.of()));\n            } else if (simplified instanceof SimpleMaterialListEntry)\n                simpleEntries.add((SimpleMaterialListEntry) simplified);\n            else\n                remainder.add(simplified);\n        });\n        return remainder;\n    }\n\n    @Override\n    protected SubMaterialListEntry createFrom(ImmutableList<MaterialListEntry<?>> subEntries, ImmutableList<SimpleMaterialListEntry> constantEntry, boolean simplified) {\n        return new AndMaterialListEntry(subEntries, constantEntry, simplified);\n    }\n\n    private static final class MaterialEntryWrapper {\n        private PeekingIterator<ImmutableMultiset<IUniqueObject<?>>> curIterator;\n        private final MaterialListEntry<?> entry;\n\n        private MaterialEntryWrapper(MaterialListEntry<?> entry) {\n            this.entry = entry;\n            this.curIterator = entry.iterator();\n        }\n\n        private ImmutableMultiset<IUniqueObject<?>> peek() {\n            return curIterator.peek();\n        }\n\n        private boolean hasNext() {\n            return curIterator.hasNext();\n        }\n\n        private void advance() {\n            curIterator.next();\n        }\n\n        private void reset() {\n            curIterator = entry.iterator();\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/inventory/materials/MaterialList.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.inventory.materials;\n\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.materials.objects.IUniqueObject;\nimport com.direwolf20.buildinggadgets.common.util.ref.JsonKeys;\nimport com.direwolf20.buildinggadgets.common.util.ref.NBTKeys;\nimport com.direwolf20.buildinggadgets.common.util.tools.JsonBiDiSerializer;\nimport com.google.common.base.Preconditions;\nimport com.google.common.collect.ImmutableList;\nimport com.google.common.collect.ImmutableMultiset;\nimport com.google.common.collect.Multiset;\nimport com.google.common.collect.PeekingIterator;\nimport com.google.gson.*;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.resources.ResourceLocation;\n\nimport javax.annotation.Nullable;\nimport java.lang.reflect.Type;\nimport java.util.Arrays;\nimport java.util.Iterator;\nimport java.util.Objects;\nimport java.util.function.Function;\n\n/**\n * Represents the required Item options for a single block, or a set of blocks. The option with the highest priority is listed\n * first, followed by the remaining options in descending priority order.\n */\npublic final class MaterialList implements Iterable<ImmutableMultiset<IUniqueObject<?>>> {\n\n    private static final MaterialList EMPTY = new MaterialList();\n\n    public static MaterialList deserialize(CompoundTag nbt, boolean persisted) {\n        return new MaterialList(readEntry(nbt, persisted));\n    }\n\n    public static MaterialList empty() {\n        return EMPTY;\n    }\n\n    public static MaterialList of() {\n        return empty();\n    }\n\n    public static MaterialList of(IUniqueObject<?>... items) {\n        return simpleBuilder().add(items).build();\n    }\n\n    public static MaterialList of(Iterable<IUniqueObject<?>> items) {\n        return simpleBuilder().addAll(items).build();\n    }\n\n    public static MaterialList and(MaterialList... materialLists) {\n        return andBuilder().add(materialLists).build();\n    }\n\n    public static MaterialList or(MaterialList... materialLists) {\n        return orBuilder().add(materialLists).build();\n    }\n\n    public static SimpleBuilder simpleBuilder() {\n        return new SimpleBuilder();\n    }\n\n    public static SubEntryBuilder andBuilder() {\n        return new SubEntryBuilder(AndMaterialListEntry::new);\n    }\n\n    public static SubEntryBuilder orBuilder() {\n        return new SubEntryBuilder(OrMaterialListEntry::new);\n    }\n\n    @Nullable\n    static MaterialListEntry.Serializer<?> getSerializerForId(ResourceLocation id) {\n        MaterialListEntry.Serializer<?> serializer = null;\n        if (id.equals(NBTKeys.SIMPLE_SERIALIZER_ID))\n            serializer = SimpleMaterialListEntry.SERIALIZER;\n        else if (id.equals(NBTKeys.OR_SERIALIZER_ID))\n            serializer = OrMaterialListEntry.SERIALIZER;\n        else if (id.equals(NBTKeys.AND_SERIALIZER_ID))\n            serializer = AndMaterialListEntry.SERIALIZER;\n        return serializer;\n    }\n\n    static MaterialListEntry<?> readEntry(CompoundTag nbt, boolean persisted) {\n        ResourceLocation id = new ResourceLocation(nbt.getString(NBTKeys.KEY_SERIALIZER));\n        MaterialListEntry.Serializer<?> serializer = getSerializerForId(id);\n        Preconditions.checkArgument(serializer != null,\n                \"Failed to recognize Serializer \" + id +\n                        \"! If you believe you need another implementation, please contact us and we can sort something out!\");\n        return serializer.readFromNBT(nbt, persisted);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    //This is ok - the only unchecked is the implicit cast for writing the data which only uses the appropriate serializer\n    static CompoundTag writeEntry(MaterialListEntry entry, boolean persisted) {\n        assert entry.getSerializer().getRegistryName() != null;\n        CompoundTag res = new CompoundTag();\n        res.putString(NBTKeys.KEY_SERIALIZER, entry.getSerializer().getRegistryName().toString());\n        res.put(NBTKeys.KEY_DATA, entry.getSerializer().writeToNBT(entry, persisted));\n        return res;\n    }\n\n    private final MaterialListEntry rootEntry;\n\n    private MaterialList() {\n        this(new SimpleMaterialListEntry(ImmutableMultiset.of()));\n    }\n\n    private MaterialList(MaterialListEntry rootEntry) {\n        this.rootEntry = Objects.requireNonNull(rootEntry, \"Cannot have a MaterialList without a root entry!\");\n    }\n\n    private MaterialListEntry getRootEntry() {\n        return rootEntry;\n    }\n\n    public Iterable<ImmutableMultiset<IUniqueObject<?>>> getItemOptions() {\n        return rootEntry instanceof SubMaterialListEntry?\n                ((SubMaterialListEntry) rootEntry).viewOnlySubEntries() :\n                ImmutableList.of();\n    }\n\n    public ImmutableMultiset<IUniqueObject<?>> getRequiredItems() {\n        SimpleMaterialListEntry simpleEntry = rootEntry instanceof SubMaterialListEntry?\n                ((SubMaterialListEntry) rootEntry).getCombinedConstantEntry() :\n                ((SimpleMaterialListEntry) rootEntry);\n        return simpleEntry.getItems();\n    }\n\n    public CompoundTag serialize(boolean persisted) {\n        return writeEntry(rootEntry, persisted);\n    }\n\n    @Override\n    @SuppressWarnings(\"unchecked\") //The iterator is independent of the type anyway\n    public PeekingIterator<ImmutableMultiset<IUniqueObject<?>>> iterator() {\n        return getRootEntry().iterator();\n    }\n\n    public static final class SubEntryBuilder {\n        private ImmutableList.Builder<MaterialListEntry<?>> subBuilder;\n        private final Function<ImmutableList<MaterialListEntry<?>>, MaterialListEntry<?>> factory;\n\n        private SubEntryBuilder(Function<ImmutableList<MaterialListEntry<?>>, MaterialListEntry<?>> factory) {\n            this.subBuilder = ImmutableList.builder();\n            this.factory = factory;\n        }\n\n        public SubEntryBuilder add(SimpleBuilder builder) {\n            return add(builder.build());\n        }\n\n        public SubEntryBuilder add(SimpleBuilder... builders) {\n            return addAllSimpleBuilders(Arrays.asList(builders));\n        }\n\n        public SubEntryBuilder add(SubEntryBuilder builder) {\n            return add(builder.build());\n        }\n\n        public SubEntryBuilder add(SubEntryBuilder... builders) {\n            return addAllSubBuilders(Arrays.asList(builders));\n        }\n\n        public SubEntryBuilder add(MaterialList element) {\n            subBuilder.add(element.getRootEntry());\n            return this;\n        }\n\n        public SubEntryBuilder add(MaterialList... elements) {\n            return addAll(Arrays.asList(elements));\n        }\n\n        public SubEntryBuilder addItems(Multiset<IUniqueObject<?>> items) {\n            subBuilder.add(new SimpleMaterialListEntry(ImmutableMultiset.copyOf(items)));\n            return this;\n        }\n\n        public SubEntryBuilder addAllItems(Iterable<? extends Multiset<IUniqueObject<?>>> iterable) {\n            iterable.forEach(this::addItems);\n            return this;\n        }\n\n        public SubEntryBuilder addAll(Iterable<MaterialList> elements) {\n            elements.forEach(this::add);\n            return this;\n        }\n\n        public SubEntryBuilder addAll(Iterator<MaterialList> elements) {\n            elements.forEachRemaining(this::add);\n            return this;\n        }\n\n        public SubEntryBuilder addAllSimpleBuilders(Iterable<SimpleBuilder> iterable) {\n            iterable.forEach(this::add);\n            return this;\n        }\n\n        public SubEntryBuilder addAllSubBuilders(Iterable<SubEntryBuilder> iterable) {\n            iterable.forEach(this::add);\n            return this;\n        }\n\n        public MaterialList build() {\n            return new MaterialList(factory.apply(subBuilder.build()).simplify());\n        }\n    }\n\n    public static final class SimpleBuilder {\n        private ImmutableMultiset.Builder<IUniqueObject<?>> requiredItems;\n\n        private SimpleBuilder() {\n            requiredItems = ImmutableMultiset.builder();\n        }\n\n        public SimpleBuilder addItem(IUniqueObject<?> item, int count) {\n            requiredItems.addCopies(item, count);\n            return this;\n        }\n\n        public SimpleBuilder addItem(IUniqueObject<?> item) {\n            return addItem(item, 1);\n        }\n\n        public SimpleBuilder addAll(Iterable<IUniqueObject<?>> items) {\n            requiredItems.addAll(items);\n            return this;\n        }\n\n        public SimpleBuilder setCount(IUniqueObject<?> element, int count) {\n            requiredItems.setCount(element, count);\n            return this;\n        }\n\n        public SimpleBuilder add(IUniqueObject<?>... elements) {\n            requiredItems.add(elements);\n            return this;\n        }\n\n        public SimpleBuilder addAll(Iterator<? extends IUniqueObject<?>> elements) {\n            requiredItems.addAll(elements);\n            return this;\n        }\n\n        public MaterialList build() {\n            return new MaterialList(new SimpleMaterialListEntry(requiredItems.build()));\n        }\n    }\n\n    public static final class JsonSerializer implements JsonBiDiSerializer<MaterialList> {\n        private final boolean printName;\n        private final boolean extended;\n\n        public JsonSerializer(boolean printName, boolean extended) {\n            this.printName = printName;\n            this.extended = extended;\n        }\n\n        @Override\n        @SuppressWarnings(\"unchecked\") // only called on the entry itself... this is ok\n        public JsonElement serialize(MaterialList src, Type typeOfSrc, JsonSerializationContext context) {\n            JsonObject res = new JsonObject();\n            res.add(JsonKeys.MATERIAL_LIST_ROOT_TYPE, context.serialize(src.getRootEntry().getSerializer().getRegistryName()));\n            res.add(JsonKeys.MATERIAL_LIST_ROOT_ENTRY, src.getRootEntry().getSerializer().asJsonSerializer(printName, extended).serialize(src.getRootEntry(), src.getRootEntry().getClass(), context));\n            return res;\n        }\n\n        @Override\n        public MaterialList deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {\n            if (json.isJsonNull())\n                return MaterialList.empty();\n            JsonObject object = json.getAsJsonObject();\n            MaterialListEntry.Serializer<?> serializer = getSerializerForId(context.deserialize(object.get(JsonKeys.MATERIAL_LIST_ROOT_TYPE), ResourceLocation.class));\n            if (serializer == null)\n                return MaterialList.empty();\n            JsonDeserializer<?> jsonSerializer = serializer.asJsonDeserializer();\n            return new MaterialList((MaterialListEntry) jsonSerializer.deserialize(object.get(JsonKeys.MATERIAL_LIST_ROOT_ENTRY), MaterialListEntry.class, context));\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/inventory/materials/MaterialListEntry.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.inventory.materials;\n\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.materials.objects.IUniqueObject;\nimport com.google.common.collect.ImmutableMultiset;\nimport com.google.common.collect.PeekingIterator;\nimport com.google.gson.JsonDeserializer;\nimport com.google.gson.JsonSerializer;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.resources.ResourceLocation;\n\n/* This is currently hidden, to avoid having yet another Registry\n - if it turns out someone needs something else then the default implementations, we can still add that and make it public\n */\ninterface MaterialListEntry<T extends MaterialListEntry<T>> extends Iterable<ImmutableMultiset<IUniqueObject<?>>> {\n    @Override\n    PeekingIterator<ImmutableMultiset<IUniqueObject<?>>> iterator();\n\n    Serializer<T> getSerializer();\n\n    MaterialListEntry<?> simplify();\n\n    interface Serializer<T extends MaterialListEntry<T>> {\n        ResourceLocation getRegistryName();\n\n        T readFromNBT(CompoundTag nbt, boolean persisted);\n\n        CompoundTag writeToNBT(T entry, boolean persisted);\n\n        JsonSerializer<T> asJsonSerializer(boolean printName, boolean extended);\n\n        JsonDeserializer<T> asJsonDeserializer();\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/inventory/materials/OrMaterialListEntry.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.inventory.materials;\n\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.materials.objects.IUniqueObject;\nimport com.direwolf20.buildinggadgets.common.util.ref.NBTKeys;\nimport com.google.common.collect.*;\n\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.List;\n\n/**\n * A {@link SubMaterialListEntry} using \"or\" as the connection between entries.\n */\nclass OrMaterialListEntry extends SubMaterialListEntry {\n    static final MaterialListEntry.Serializer<SubMaterialListEntry> SERIALIZER = new SubMaterialListEntry.Serializer(NBTKeys.OR_SERIALIZER_ID) {\n        @Override\n        protected SubMaterialListEntry create(ImmutableList<MaterialListEntry<?>> subEntries, ImmutableList<SimpleMaterialListEntry> constantEntries) {\n            return new OrMaterialListEntry(subEntries, constantEntries);\n        }\n    };\n\n    OrMaterialListEntry(ImmutableList<MaterialListEntry<?>> subEntries, ImmutableList<SimpleMaterialListEntry> simpleEntries, boolean simplified) {\n        super(subEntries, simpleEntries, simplified);\n    }\n\n    OrMaterialListEntry(ImmutableList<MaterialListEntry<?>> subEntries, ImmutableList<SimpleMaterialListEntry> constantEntries) {\n        super(subEntries, constantEntries);\n    }\n\n    OrMaterialListEntry(ImmutableList<MaterialListEntry<?>> subEntries) {\n        super(subEntries);\n    }\n\n    /**\n     * Applies an \"or\" by iterating over all contained entries in the order in which they appear, and then in-turn iterating over the items.\n     */\n    @Override\n    public PeekingIterator<ImmutableMultiset<IUniqueObject<?>>> iterator() {\n        if (! getAllSubEntries().findFirst().isPresent())\n            return Iterators.peekingIterator(Iterators.singletonIterator(ImmutableMultiset.of()));\n        Iterator<MaterialListEntry<?>> entryIterator = getAllSubEntries().iterator();\n        return Iterators.peekingIterator(new AbstractIterator<ImmutableMultiset<IUniqueObject<?>>>() {\n            private Iterator<ImmutableMultiset<IUniqueObject<?>>> itemIterator;\n\n            @Override\n            protected ImmutableMultiset<IUniqueObject<?>> computeNext() {\n                if (itemIterator == null) {\n                    if (entryIterator.hasNext())\n                        itemIterator = entryIterator.next().iterator();\n                    else\n                        return endOfData();\n                }\n                if (! itemIterator.hasNext()) {\n                    itemIterator = null;\n                    return computeNext();\n                }\n                return itemIterator.next();\n            }\n        });\n    }\n\n    @Override\n    public MaterialListEntry.Serializer<SubMaterialListEntry> getSerializer() {\n        return SERIALIZER;\n    }\n\n    @Override\n    protected List<MaterialListEntry<?>> orderAndSimplifyEntries(List<OrMaterialListEntry> orEntries,\n                                                                 List<AndMaterialListEntry> andEntries,\n                                                                 List<SimpleMaterialListEntry> simpleEntries) {\n        List<MaterialListEntry<?>> remainder = new ArrayList<>();\n        //Theoretically we could evaluate the Set intersection here and add that as a constant entry\n        //I believe that to be too much overhead for too little gain though\n        getSubEntries().stream().map(MaterialListEntry::simplify).forEach(entry -> {\n            if (entry instanceof AndMaterialListEntry)\n                andEntries.add((AndMaterialListEntry) entry);\n            else if (entry instanceof OrMaterialListEntry) {\n                simpleEntries.addAll(((SubMaterialListEntry) entry).getConstantEntries());\n                pullUpInnerEntries((SubMaterialListEntry) entry, orEntries, andEntries, simpleEntries, remainder);\n            } else //Cannot pull out simple Entries! They are alternatives after all!\n                remainder.add(entry);\n        });\n        return remainder;\n    }\n\n    @Override\n    protected SubMaterialListEntry createFrom(ImmutableList<MaterialListEntry<?>> subEntries, ImmutableList<SimpleMaterialListEntry> constantEntry, boolean simplified) {\n        return new OrMaterialListEntry(subEntries, constantEntry, simplified);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/inventory/materials/SimpleMaterialListEntry.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.inventory.materials;\n\nimport com.direwolf20.buildinggadgets.common.BuildingGadgets;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.materials.objects.IUniqueObject;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.materials.objects.IUniqueObjectSerializer;\nimport com.direwolf20.buildinggadgets.common.tainted.registry.Registries;\nimport com.direwolf20.buildinggadgets.common.util.ref.JsonKeys;\nimport com.direwolf20.buildinggadgets.common.util.ref.NBTKeys;\nimport com.direwolf20.buildinggadgets.common.util.tools.RegistryUtils;\nimport com.google.common.collect.*;\nimport com.google.common.collect.Multiset.Entry;\nimport com.google.gson.*;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.nbt.ListTag;\nimport net.minecraft.nbt.Tag;\nimport net.minecraft.resources.ResourceLocation;\n\nimport java.util.Comparator;\nimport java.util.Objects;\n\nclass SimpleMaterialListEntry implements MaterialListEntry<SimpleMaterialListEntry> {\n    static final MaterialListEntry.Serializer<SimpleMaterialListEntry> SERIALIZER = new Serializer();\n    private final ImmutableMultiset<IUniqueObject<?>> items;\n\n    SimpleMaterialListEntry(ImmutableMultiset<IUniqueObject<?>> items) {\n        this.items = Objects.requireNonNull(items, \"Cannot have a SimpleMaterialListEntry without any Materials!\");\n    }\n\n    ImmutableMultiset<IUniqueObject<?>> getItems() {\n        return items;\n    }\n\n    @Override\n    public PeekingIterator<ImmutableMultiset<IUniqueObject<?>>> iterator() {\n        return Iterators.peekingIterator(Iterators.singletonIterator(items));\n    }\n\n    @Override\n    public MaterialListEntry.Serializer<SimpleMaterialListEntry> getSerializer() {\n        return SERIALIZER;\n    }\n\n    @Override\n    public SimpleMaterialListEntry simplify() {\n        return this;\n    }\n\n    private static class Serializer implements MaterialListEntry.Serializer<SimpleMaterialListEntry> {\n        private static final Comparator<Entry<IUniqueObject<?>>> COMPARATOR = Comparator\n                .<Entry<IUniqueObject<?>>, ResourceLocation>comparing(e -> e.getElement().getObjectRegistryName())\n                .thenComparingInt(Entry::getCount);\n\n        @Override\n        public SimpleMaterialListEntry readFromNBT(CompoundTag nbt, boolean persisted) {\n            ListTag nbtList = nbt.getList(NBTKeys.KEY_DATA, Tag.TAG_COMPOUND);\n            ImmutableMultiset.Builder<IUniqueObject<?>> builder = ImmutableMultiset.builder();\n            for (Tag nbtEntry : nbtList) {\n                CompoundTag compoundEntry = (CompoundTag) nbtEntry;\n                IUniqueObjectSerializer serializer = persisted ?\n                        RegistryUtils.getFromString(Registries.UNIQUE_DATA_SERIALIZER_REGISTRY.get(), compoundEntry.getString(NBTKeys.KEY_SERIALIZER)) :\n                        RegistryUtils.getById(Registries.UNIQUE_DATA_SERIALIZER_REGISTRY.get(), compoundEntry.getInt(NBTKeys.KEY_SERIALIZER));\n                if (serializer == null) {\n                    BuildingGadgets.LOG.error(\"Found unknown UniqueItem serializer {}. Skipping!\", compoundEntry.getString(NBTKeys.KEY_SERIALIZER));\n                    continue;\n                }\n                builder.addCopies(\n                        serializer.deserialize((compoundEntry.getCompound(NBTKeys.KEY_DATA))),\n                        compoundEntry.getInt(NBTKeys.KEY_COUNT));\n            }\n            return new SimpleMaterialListEntry(builder.build());\n        }\n\n        @Override\n        public CompoundTag writeToNBT(SimpleMaterialListEntry listEntry, boolean persisted) {\n            CompoundTag res = new CompoundTag();\n            ListTag nbtList = new ListTag();\n            for (Entry<IUniqueObject<?>> entry : listEntry.getItems().entrySet()) {\n                CompoundTag nbtEntry = new CompoundTag();\n                if (persisted)\n                    nbtEntry.putString(NBTKeys.KEY_SERIALIZER, Registries.UNIQUE_DATA_SERIALIZER_REGISTRY.get().getKey(entry.getElement().getSerializer()).toString());\n                else\n                    nbtEntry.putInt(NBTKeys.KEY_SERIALIZER, RegistryUtils.getId(Registries.UNIQUE_DATA_SERIALIZER_REGISTRY.get(), entry.getElement().getSerializer()));\n                nbtEntry.put(NBTKeys.KEY_DATA, entry.getElement().getSerializer().serialize(entry.getElement(), persisted));\n                nbtEntry.putInt(NBTKeys.KEY_COUNT, entry.getCount());\n                nbtList.add(nbtEntry);\n            }\n            res.put(NBTKeys.KEY_DATA, nbtList);\n            return res;\n        }\n\n        @Override\n        public JsonSerializer<SimpleMaterialListEntry> asJsonSerializer(boolean printName, boolean extended) {\n            return (src, typeOfSrc, context) -> {\n                Multiset<IUniqueObject<?>> set = src.getItems();\n                JsonArray jsonArray = new JsonArray();\n                for (Entry<IUniqueObject<?>> entry : ImmutableList.sortedCopyOf(COMPARATOR, set.entrySet())) {\n                    JsonElement element = entry.getElement()\n                            .getSerializer()\n                            .asJsonSerializer(printName, extended)\n                            .serialize(entry.getElement(), entry.getElement().getClass(), context);\n                    JsonObject obj = new JsonObject();\n                    obj.add(JsonKeys.MATERIAL_LIST_ITEM_TYPE, context.serialize(Registries.UNIQUE_DATA_SERIALIZER_REGISTRY.get().getKey(entry.getElement().getSerializer())));\n                    obj.addProperty(JsonKeys.MATERIAL_LIST_ITEM_COUNT, entry.getCount());\n                    obj.add(JsonKeys.MATERIAL_LIST_ITEM, element);\n                    jsonArray.add(obj);\n                }\n                return jsonArray;\n            };\n        }\n\n        @Override\n        public JsonDeserializer<SimpleMaterialListEntry> asJsonDeserializer() {\n            return (json, typeOfT, context) -> {\n                JsonArray array = json.getAsJsonArray();\n                ImmutableMultiset.Builder<IUniqueObject<?>> items = ImmutableMultiset.builder();\n                for (JsonElement element : array) {\n                    JsonObject object = element.getAsJsonObject();\n                    ResourceLocation id = context.deserialize(object.get(JsonKeys.MATERIAL_LIST_ITEM_TYPE), ResourceLocation.class);\n                    IUniqueObjectSerializer serializer = Registries.UNIQUE_DATA_SERIALIZER_REGISTRY.get().getValue(id);\n                    if (serializer == null)\n                        continue;\n                    int count = object.getAsJsonPrimitive(JsonKeys.MATERIAL_LIST_ITEM_COUNT).getAsInt();\n                    IUniqueObject<?> item = serializer.asJsonDeserializer().deserialize(object.get(JsonKeys.MATERIAL_LIST_ITEM), IUniqueObject.class, context);\n                    items.addCopies(item, count);\n                }\n                return new SimpleMaterialListEntry(items.build());\n            };\n        }\n\n        @Override\n        public ResourceLocation getRegistryName() {\n            return NBTKeys.SIMPLE_SERIALIZER_ID;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/inventory/materials/SubMaterialListEntry.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.inventory.materials;\n\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.materials.objects.IUniqueObject;\nimport com.direwolf20.buildinggadgets.common.util.ref.JsonKeys;\nimport com.direwolf20.buildinggadgets.common.util.ref.NBTKeys;\nimport com.google.common.collect.ImmutableList;\nimport com.google.common.collect.ImmutableMultiset;\nimport com.google.common.collect.Streams;\nimport com.google.gson.*;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.nbt.Tag;\nimport net.minecraft.nbt.ListTag;\nimport net.minecraft.resources.ResourceLocation;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.stream.Stream;\n\nabstract class SubMaterialListEntry implements MaterialListEntry<SubMaterialListEntry> {\n    private final ImmutableList<MaterialListEntry<?>> subEntries;\n    private final ImmutableList<SimpleMaterialListEntry> constantEntries;\n    private boolean simplified;\n\n    public SubMaterialListEntry(ImmutableList<MaterialListEntry<?>> subEntries, ImmutableList<SimpleMaterialListEntry> simpleEntries, boolean simplified) {\n        this.subEntries = Objects.requireNonNull(subEntries, \"Cannot construct a SubMaterialListEntry without a list of Sub-MaterialEntries!\");\n        this.constantEntries = Objects.requireNonNull(simpleEntries, \"Cannot construct a SubMaterialListEntry without a list of constant Entries!\");\n        this.simplified = simplified;\n    }\n\n    protected SubMaterialListEntry(ImmutableList<MaterialListEntry<?>> subEntries, ImmutableList<SimpleMaterialListEntry> simpleEntries) {\n        this(subEntries, simpleEntries, false);\n    }\n\n    public SubMaterialListEntry(ImmutableList<MaterialListEntry<?>> subEntries) {\n        this(subEntries, ImmutableList.of());\n    }\n\n    protected Stream<MaterialListEntry<?>> getAllSubEntries() {\n        return Streams.concat(subEntries.stream(), constantEntries.stream());\n    }\n\n    @Override\n    public MaterialListEntry<?> simplify() {\n        if (simplified)\n            return this;\n        List<OrMaterialListEntry> orEntries = new ArrayList<>(subEntries.size());\n        List<AndMaterialListEntry> andEntries = new ArrayList<>(subEntries.size());\n        List<SimpleMaterialListEntry> simpleEntries = new ArrayList<>(constantEntries);\n        List<MaterialListEntry<?>> remainder = orderAndSimplifyEntries(orEntries, andEntries, simpleEntries);\n        SimpleMaterialListEntry constantEntry = combine(simpleEntries);\n        if (orEntries.isEmpty() && andEntries.isEmpty() && remainder.isEmpty())\n            return constantEntry;\n        return createFrom(ImmutableList.<MaterialListEntry<?>>builder()\n                .addAll(andEntries)\n                .addAll(orEntries)\n                .addAll(remainder)\n                .build(),\n                constantEntry.getItems().isEmpty() ? ImmutableList.of() : ImmutableList.of(constantEntry), true);\n    }\n\n    protected abstract List<MaterialListEntry<?>> orderAndSimplifyEntries(\n            List<OrMaterialListEntry> orEntries,\n            List<AndMaterialListEntry> andEntries,\n            List<SimpleMaterialListEntry> simpleEntries);\n\n    protected ImmutableList<MaterialListEntry<?>> getSubEntries() {\n        return subEntries;\n    }\n\n    protected ImmutableList<SimpleMaterialListEntry> getConstantEntries() {\n        return constantEntries;\n    }\n\n    protected SimpleMaterialListEntry getCombinedConstantEntry() {\n        return combine(constantEntries);\n    }\n\n    private SimpleMaterialListEntry combine(List<SimpleMaterialListEntry> simpleEntries) {\n        if (simpleEntries.size() == 1)\n            return simpleEntries.get(0);\n        ImmutableMultiset.Builder<IUniqueObject<?>> builder = ImmutableMultiset.builder();\n        for (SimpleMaterialListEntry entry:simpleEntries) {\n            builder.addAll(entry.getItems());\n        };\n        return new SimpleMaterialListEntry(builder.build());\n    }\n\n    protected abstract SubMaterialListEntry createFrom(ImmutableList<MaterialListEntry<?>> subEntries, ImmutableList<SimpleMaterialListEntry> constantEntry, boolean simplified);\n\n    protected Iterable<ImmutableMultiset<IUniqueObject<?>>> viewOnlySubEntries() {\n        return createFrom(getSubEntries(), ImmutableList.of(), simplified);\n    }\n\n    protected void pullUpInnerEntries(SubMaterialListEntry entry,\n                                      List<OrMaterialListEntry> orEntries,\n                                      List<AndMaterialListEntry> andEntries,\n                                      List<SimpleMaterialListEntry> simpleEntries,\n                                      List<MaterialListEntry<?>> remainder) {\n        for (MaterialListEntry<?> subEntry : entry.getSubEntries()) {\n            if (subEntry instanceof OrMaterialListEntry)\n                orEntries.add((OrMaterialListEntry) subEntry);\n            else if (subEntry instanceof AndMaterialListEntry)\n                andEntries.add((AndMaterialListEntry) subEntry);\n            else if (subEntry instanceof SimpleMaterialListEntry)\n                simpleEntries.add((SimpleMaterialListEntry) subEntry);\n            else\n                remainder.add(subEntry);\n        } ;\n    }\n\n    protected static abstract class Serializer implements MaterialListEntry.Serializer<SubMaterialListEntry> {\n        private final ResourceLocation registryName;\n\n        public Serializer(ResourceLocation registryName) {\n            this.registryName = registryName;\n        }\n\n        @Override\n        public ResourceLocation getRegistryName() {\n            return registryName;\n        }\n\n        @Override\n        public SubMaterialListEntry readFromNBT(CompoundTag nbt, boolean persisted) {\n            ListTag list = nbt.getList(NBTKeys.KEY_SUB_ENTRIES, Tag.TAG_COMPOUND);\n            ImmutableList.Builder<MaterialListEntry<?>> entryBuilder = ImmutableList.builder();\n            ImmutableList.Builder<SimpleMaterialListEntry> simpleBuilder = ImmutableList.builder();\n            for (Tag subEntry : list) {\n                MaterialListEntry<?> entry = MaterialList.readEntry((CompoundTag) subEntry, persisted);\n                if (entry instanceof SimpleMaterialListEntry)\n                    simpleBuilder.add((SimpleMaterialListEntry) entry);\n                else\n                    entryBuilder.add(entry);\n            }\n            return create(entryBuilder.build(), simpleBuilder.build());\n        }\n\n        @Override\n        public CompoundTag writeToNBT(SubMaterialListEntry entry, boolean persisted) {\n            ListTag list = new ListTag();\n            entry.getAllSubEntries()\n                    .map(subEntry -> MaterialList.writeEntry(subEntry, persisted))\n                    .forEach(list::add);\n            CompoundTag nbt = new CompoundTag();\n            nbt.put(NBTKeys.KEY_SUB_ENTRIES, list);\n            return nbt;\n        }\n\n        @Override\n        public JsonSerializer<SubMaterialListEntry> asJsonSerializer(boolean printName, boolean extended) {\n            return (src, typeOfSrc, context) -> {\n                JsonArray ar = new JsonArray();\n                src.getAllSubEntries().forEach((MaterialListEntry entry) -> {\n                    @SuppressWarnings(\"unchecked\") //I ignore generics on purpose here, as this will always be the correct type - it's it's own serializer\n                    JsonElement element = entry.getSerializer().asJsonSerializer(printName, extended).serialize(entry, entry.getClass(), context);\n                    JsonObject obj = new JsonObject();\n                    obj.add(JsonKeys.MATERIAL_ENTRY_TYPE, context.serialize(entry.getSerializer().getRegistryName()));\n                    if (element.isJsonArray())\n                        obj.add(JsonKeys.MATERIAL_ENTRIES, element.getAsJsonArray());\n                    else\n                        obj.add(JsonKeys.MATERIAL_ENTRY, element);\n                    ar.add(obj);\n                });\n                return ar;\n            };\n        }\n\n        @Override\n        public JsonDeserializer<SubMaterialListEntry> asJsonDeserializer() {\n            return (json, typeOfT, context) -> {\n                JsonArray array = json.getAsJsonArray();\n                ImmutableList.Builder<MaterialListEntry<?>> subEntries = ImmutableList.builder();\n                for (JsonElement element : array) {\n                    JsonObject object = element.getAsJsonObject();\n                    ResourceLocation id = context.deserialize(object.get(JsonKeys.MATERIAL_ENTRY_TYPE), ResourceLocation.class);\n                    MaterialListEntry.Serializer<?> serializer = MaterialList.getSerializerForId(id);\n                    if (serializer == null)\n                        continue;\n                    JsonElement subEntry = object.has(JsonKeys.MATERIAL_ENTRIES) ? object.get(JsonKeys.MATERIAL_ENTRIES) : object.get(JsonKeys.MATERIAL_ENTRY);\n                    subEntries.add(serializer.asJsonDeserializer().deserialize(subEntry, MaterialListEntry.class, context));\n                }\n                MaterialListEntry<?> entry = create(subEntries.build(), ImmutableList.of()).simplify();\n                if (entry instanceof SubMaterialListEntry)\n                    return (SubMaterialListEntry) entry;\n                return new AndMaterialListEntry(ImmutableList.of(entry));\n            };\n\n        }\n\n        protected abstract SubMaterialListEntry create(ImmutableList<MaterialListEntry<?>> subEntries, ImmutableList<SimpleMaterialListEntry> constantEntries);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/inventory/materials/objects/IUniqueObject.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.inventory.materials.objects;\n\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.handle.IObjectHandle;\nimport net.minecraft.resources.ResourceLocation;\nimport net.minecraft.world.item.Item;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.ItemLike;\nimport net.minecraftforge.registries.ForgeRegistries;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\n\n/**\n * Some sort of Object that can be interpreted as a RequiredMaterial.\n * Because the Object classes are indexed for performance reasons, this must report the index class and object!\n *\n * @param <T>\n */\npublic interface IUniqueObject<T> {\n    Class<T> getIndexClass();\n\n    T getIndexObject();\n\n    boolean matches(ItemStack stack);\n\n    ItemStack insertInto(ItemStack stack, int count);\n\n    //whether or not this Item prefers to insert via tryCreateInsertStack or via insertInto\n    //while insertInto is more generic, it prevents spawning of Items or directly dropping into the player inventory\n    //returning false is suggested for all non-Item unique objects, like fluids\n    default boolean preferStackInsert() {\n        return getIndexClass() == Item.class;\n    }\n\n    default Optional<ItemStack> tryCreateInsertStack(Map<Class<?>, Map<Object, List<IObjectHandle<?>>>> index, int count) {\n        return Optional.of(createStack(count));\n    }\n\n    default ItemStack createStack() {\n        return createStack(1);\n    }\n\n    //used for writing material-list to a json-String\n    default ResourceLocation getObjectRegistryName() {\n        T indexObj = getIndexObject();\n        if (indexObj instanceof ItemLike item)\n            return ForgeRegistries.ITEMS.getKey(item.asItem());\n\n        throw new RuntimeException(\"Unable to configure name for unique object\");\n    }\n\n    ItemStack createStack(int count);\n\n    IUniqueObjectSerializer getSerializer();\n\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/inventory/materials/objects/IUniqueObjectSerializer.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.inventory.materials.objects;\n\nimport com.google.gson.JsonDeserializer;\nimport com.google.gson.JsonSerializer;\nimport net.minecraft.nbt.CompoundTag;\n\npublic interface IUniqueObjectSerializer {\n    CompoundTag serialize(IUniqueObject<?> item, boolean persisted);\n\n    IUniqueObject<?> deserialize(CompoundTag res);\n\n    JsonSerializer<IUniqueObject<?>> asJsonSerializer(boolean printName, boolean extended);\n\n    JsonDeserializer<IUniqueObject<?>> asJsonDeserializer();\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/inventory/materials/objects/UniqueItem.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.inventory.materials.objects;\n\nimport com.direwolf20.buildinggadgets.common.tainted.template.SerialisationSupport;\nimport com.direwolf20.buildinggadgets.common.util.ref.JsonKeys;\nimport com.direwolf20.buildinggadgets.common.util.ref.NBTKeys;\nimport com.direwolf20.buildinggadgets.common.util.tools.RegistryUtils;\nimport com.google.common.base.MoreObjects;\nimport com.google.common.base.Preconditions;\nimport com.google.gson.JsonDeserializer;\nimport com.google.gson.JsonObject;\nimport com.google.gson.JsonSerializer;\nimport com.mojang.brigadier.exceptions.CommandSyntaxException;\nimport it.unimi.dsi.fastutil.bytes.Byte2ObjectMap;\nimport it.unimi.dsi.fastutil.bytes.Byte2ObjectOpenHashMap;\nimport net.minecraft.client.resources.language.I18n;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.nbt.Tag;\nimport net.minecraft.nbt.TagParser;\nimport net.minecraft.resources.ResourceLocation;\nimport net.minecraft.world.item.Item;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.item.Items;\nimport net.minecraftforge.registries.ForgeRegistries;\n\nimport javax.annotation.Nullable;\nimport java.util.Arrays;\nimport java.util.Objects;\n\n/**\n * An {@link IUniqueObject} which represents all defining (unique) properties of an ItemStack: the item and the 2 types of nbt.\n * For convenience 2 match types are provided for the nbt data.\n */\npublic final class UniqueItem implements IUniqueObject<Item> {\n    public enum ComparisonMode {\n        EXACT_MATCH(0) {\n            @Override\n            public boolean match(CompoundTag nbt, @Nullable CompoundTag other) {\n                return nbt.equals(other);\n            }\n        },\n        SUB_TAG_MATCH(1) {\n            @Override\n            public boolean match(CompoundTag nbt, @Nullable CompoundTag other) {\n                if (other == null)\n                    return false;\n                for (String key : nbt.getAllKeys()) {\n                    Tag val = nbt.get(key);\n                    if (val == null) {\n                        if (other.get(key) != null) return false;\n                        else continue;\n                    }\n                    if (!other.contains(key, val.getId()) || other.get(key) == null)\n                        return false;\n                    if (val.getId() == Tag.TAG_COMPOUND && !match((CompoundTag) val, other.getCompound(key)))\n                        return false;\n                    else if (val.getId() != Tag.TAG_COMPOUND && !val.getAsString().equals(other.get(key).getAsString()))\n                        return false;\n                }\n                return true;\n            }\n        };\n        private static final Byte2ObjectMap<ComparisonMode> BY_ID = new Byte2ObjectOpenHashMap<>();\n        private final byte id;\n\n        ComparisonMode(int id) {\n            this.id = (byte) id;\n        }\n\n        public byte getId() {\n            return id;\n        }\n\n        public abstract boolean match(CompoundTag nbt, @Nullable CompoundTag other);\n\n        public static ComparisonMode byId(byte id) {\n            ComparisonMode mode = BY_ID.get(id);\n            return mode == null ? EXACT_MATCH : mode;//prevent future additions from crashing older clients...\n        }\n\n        static {\n            Arrays.stream(values()).forEach(m -> BY_ID.put(m.getId(), m));\n        }\n    }\n\n    public static UniqueItem ofStack(ItemStack stack) {\n        CompoundTag nbt = new CompoundTag();\n        stack.save(nbt);\n        return new UniqueItem(stack.getItem(), stack.getTag(), ComparisonMode.EXACT_MATCH, nbt.getCompound(\"ForgeCaps\"), ComparisonMode.EXACT_MATCH);\n    }\n\n    private final Item item;\n    @Nullable\n    private final CompoundTag tagCompound;\n    @Nullable\n    private final CompoundTag forgeCaps;\n    private final int hash;\n    private final ComparisonMode tagMatch;\n    private final ComparisonMode capMatch;\n\n    public UniqueItem(Item item) {\n        this(item, null, ComparisonMode.EXACT_MATCH);\n    }\n\n    public UniqueItem(Item item, @Nullable CompoundTag tagCompound, ComparisonMode comparisonMode) {\n        this(item, tagCompound, comparisonMode, null, ComparisonMode.EXACT_MATCH);\n    }\n\n    public UniqueItem(Item item, @Nullable CompoundTag tagCompound, ComparisonMode tagMatch, @Nullable CompoundTag forgeCaps, ComparisonMode capMatch) {\n        this.item = Objects.requireNonNull(item, \"Cannot construct a UniqueItem for a null Item!\");\n        this.tagCompound = tagCompound;\n        this.forgeCaps = forgeCaps;\n        this.tagMatch = Objects.requireNonNull(tagMatch);\n        this.capMatch = Objects.requireNonNull(capMatch);\n        int hash = capMatch.hashCode() + 31 * tagMatch.hashCode();\n        hash = tagCompound != null ? tagCompound.hashCode() + 31 * hash : hash;\n        hash = forgeCaps != null ? forgeCaps.hashCode() + 31 * hash : hash;\n        this.hash = RegistryUtils.getItemId(item).hashCode() + 31 * hash;\n    }\n\n    @Override\n    public Class<Item> getIndexClass() {\n        return Item.class;\n    }\n\n    @Override\n    public Item getIndexObject() {\n        return item;\n    }\n\n    @Nullable\n    public CompoundTag getTag() {\n        return tagCompound != null ? tagCompound.copy() : null;\n    }\n\n    @Nullable\n    public CompoundTag getForgeCaps() {\n        return forgeCaps != null ? forgeCaps.copy() : null;\n    }\n\n    @Override\n    public ItemStack createStack(int count) {\n        ItemStack res = new ItemStack(item, count, forgeCaps);\n        res.setTag(tagCompound);\n        return res;\n    }\n\n    @Override\n    public boolean matches(ItemStack stack) {\n        if (stack.getItem() != getIndexObject())\n            return false;\n        if (tagCompound != null && !tagMatch.match(tagCompound, stack.getTag()))\n            return false;\n        if (forgeCaps != null) {\n            //There's no getter for this, so we need to hack or way into it...\n            CompoundTag container = new CompoundTag();\n            stack.save(container);\n            CompoundTag otherCapNBT = container.getCompound(\"ForgeCaps\");\n            return capMatch.match(forgeCaps, otherCapNBT);\n        }\n        return true;\n    }\n\n    @Override\n    public ItemStack insertInto(ItemStack stack, int count) {\n        if (forgeCaps != null) {\n            stack = new ItemStack(getIndexObject(), count, forgeCaps);\n        } else\n            stack.setCount(Math.min(stack.getCount() + count, stack.getMaxStackSize()));\n        if (tagCompound != null)\n            stack.setTag(tagCompound);\n        return stack;\n    }\n\n    @Override\n    public IUniqueObjectSerializer getSerializer() {\n        return SerialisationSupport.uniqueItemSerializer();\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) return true;\n        if (!(o instanceof UniqueItem)) return false;\n        UniqueItem that = (UniqueItem) o;\n        if (!item.equals(that.item)) return false;\n        if (tagCompound != null ? !tagCompound.equals(that.tagCompound) : that.tagCompound != null) return false;\n        if (forgeCaps != null ? !forgeCaps.equals(that.forgeCaps) : that.forgeCaps != null) return false;\n        if (tagMatch != that.tagMatch) return false;\n        return capMatch == that.capMatch;\n    }\n\n    @Override\n    public int hashCode() {\n        return hash;\n    }\n\n    @Override\n    public String toString() {\n        return MoreObjects.toStringHelper(this)\n                .add(\"item\", RegistryUtils.getItemId(getIndexObject()))\n                .add(\"tagCompound\", tagCompound)\n                .add(\"forgeCaps\", forgeCaps)\n                .add(\"tagMatch\", tagMatch)\n                .add(\"capMatch\", capMatch)\n                .toString();\n    }\n\n    public static final class Serializer implements IUniqueObjectSerializer {\n        @Override\n        public CompoundTag serialize(IUniqueObject<?> obj, boolean persisted) {\n            UniqueItem item = (UniqueItem) obj;\n            CompoundTag res = new CompoundTag();\n            if (item.tagCompound != null)\n                res.put(NBTKeys.KEY_DATA, item.tagCompound);\n            if (item.forgeCaps != null)\n                res.put(NBTKeys.KEY_CAP_NBT, item.forgeCaps);\n            if (persisted)\n                res.putString(NBTKeys.KEY_ID, RegistryUtils.getItemId(item.getIndexObject()).toString());\n            else\n                res.putInt(NBTKeys.KEY_ID, RegistryUtils.getId(ForgeRegistries.ITEMS, item.item));\n            res.putByte(NBTKeys.KEY_DATA_COMPARISON, item.tagMatch.getId());\n            res.putByte(NBTKeys.KEY_CAP_COMPARISON, item.capMatch.getId());\n            return res;\n        }\n\n        @Override\n        public IUniqueObject<Item> deserialize(CompoundTag res) {\n            Preconditions.checkArgument(res.contains(NBTKeys.KEY_ID), \"Cannot construct a UniqueItem without an Item!\");\n            CompoundTag nbt = res.getCompound(NBTKeys.KEY_DATA);\n            ComparisonMode mode = ComparisonMode.byId(res.getByte(NBTKeys.KEY_DATA_COMPARISON));\n            CompoundTag capNbt = res.getCompound(NBTKeys.KEY_CAP_NBT);\n            ComparisonMode capMode = ComparisonMode.byId(res.getByte(NBTKeys.KEY_CAP_COMPARISON));\n            Item item;\n            if (res.contains(NBTKeys.KEY_ID, Tag.TAG_INT))\n                item = RegistryUtils.getById(ForgeRegistries.ITEMS, res.getInt(NBTKeys.KEY_ID));\n            else\n                item = ForgeRegistries.ITEMS.getValue(new ResourceLocation(res.getString(NBTKeys.KEY_ID)));\n            return new UniqueItem(item, nbt.isEmpty() ? null : nbt, mode, capNbt.isEmpty() ? null : capNbt, capMode);\n        }\n\n        @Override\n        public JsonSerializer<IUniqueObject<?>> asJsonSerializer(boolean printName, boolean extended) {\n            return (uobj, typeOfSrc, context) -> {\n                JsonObject obj = new JsonObject();\n                UniqueItem element = (UniqueItem) uobj;\n                Item item = element.getIndexObject();\n                if (printName)\n                    obj.addProperty(JsonKeys.MATERIAL_LIST_ITEM_NAME, I18n.get(item.getDescriptionId(element.createStack())));\n                obj.add(JsonKeys.MATERIAL_LIST_ITEM_ID, context.serialize(RegistryUtils.getItemId(element.getIndexObject())));\n                if (extended) {\n                    if (element.tagCompound != null && !element.tagCompound.isEmpty()) {\n                        obj.addProperty(JsonKeys.MATERIAL_LIST_ITEM_NBT, element.tagCompound.toString());\n                        obj.add(JsonKeys.MATERIAL_LIST_ITEM_NBT_MATCH, context.serialize(element.tagMatch));\n                    }\n                    if (element.forgeCaps != null && !element.forgeCaps.isEmpty()) {\n                        obj.addProperty(JsonKeys.MATERIAL_LIST_CAP_NBT, element.forgeCaps.toString());\n                        obj.add(JsonKeys.MATERIAL_LIST_CAP_NBT_MATCH, context.serialize(element.capMatch));\n                    }\n                }\n                return obj;\n            };\n        }\n\n        @Override\n        public JsonDeserializer<IUniqueObject<?>> asJsonDeserializer() {\n            return (json, typeOfT, context) -> {\n                JsonObject object = json.getAsJsonObject();\n                ResourceLocation registryName = context.deserialize(object.get(JsonKeys.MATERIAL_LIST_ITEM_ID), ResourceLocation.class);\n                Item item = ForgeRegistries.ITEMS.getValue(registryName);\n                if (item == null)\n                    return new UniqueItem(Items.AIR);\n                CompoundTag tagCompound = null;\n                ComparisonMode tagMatch = ComparisonMode.EXACT_MATCH;\n                if (object.has(JsonKeys.MATERIAL_LIST_ITEM_NBT)) {\n                    try {\n                        tagCompound = TagParser.parseTag(object.getAsJsonPrimitive(JsonKeys.MATERIAL_LIST_ITEM_NBT).getAsString());\n                        tagMatch = context.deserialize(object.get(JsonKeys.MATERIAL_LIST_ITEM_NBT_MATCH), ComparisonMode.class);\n                    } catch (CommandSyntaxException e) {\n                        e.printStackTrace();\n                    }\n                }\n                CompoundTag forgeCaps = null;\n                ComparisonMode capMatch = ComparisonMode.EXACT_MATCH;\n                if (object.has(JsonKeys.MATERIAL_LIST_CAP_NBT)) {\n                    try {\n                        forgeCaps = TagParser.parseTag(object.getAsJsonPrimitive(JsonKeys.MATERIAL_LIST_CAP_NBT).getAsString());\n                        capMatch = context.deserialize(object.get(JsonKeys.MATERIAL_LIST_CAP_NBT_MATCH), ComparisonMode.class);\n                    } catch (CommandSyntaxException e) {\n                        e.printStackTrace();\n                    }\n                }\n                return new UniqueItem(item, tagCompound, tagMatch, forgeCaps, capMatch);\n            };\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/inventory/materials/package-info.java",
    "content": "@ParametersAreNonnullByDefault\n@MethodsReturnNonnullByDefault\npackage com.direwolf20.buildinggadgets.common.tainted.inventory.materials;\n\nimport net.minecraft.MethodsReturnNonnullByDefault;\n\nimport javax.annotation.ParametersAreNonnullByDefault;"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/package-info.java",
    "content": "@Tainted(reason = \"Entire packaged contains a vast majority of tainted code due to half finished systems\")\npackage com.direwolf20.buildinggadgets.common.tainted;"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/registry/ImmutableOrderedRegistry.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.registry;\n\nimport com.google.common.collect.ImmutableBiMap;\nimport com.google.common.collect.ImmutableList;\nimport net.minecraft.resources.ResourceLocation;\n\nimport javax.annotation.Nullable;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Spliterator;\nimport java.util.function.Consumer;\nimport java.util.stream.Stream;\n\npublic final class ImmutableOrderedRegistry<T> {\n    private final ImmutableBiMap<ResourceLocation, T> backingMap;\n    private final ImmutableList<T> orderedValues;\n\n    ImmutableOrderedRegistry(Map<ResourceLocation, T> backingMap, List<T> orderedValues) {\n        this.backingMap = ImmutableBiMap.copyOf(backingMap);\n        this.orderedValues = ImmutableList.copyOf(orderedValues);\n    }\n\n    @Nullable\n    public T get(ResourceLocation key) {\n        return backingMap.get(key);\n    }\n\n    public boolean contains(ResourceLocation key) {\n        return backingMap.containsKey(key);\n    }\n\n    public ImmutableList<T> getValuesInOrder() {\n        return orderedValues;\n    }\n\n    public Stream<T> values() {\n        return orderedValues.stream();\n    }\n\n    public Iterator<T> iterator() {\n        return orderedValues.iterator();\n    }\n\n    public void forEach(Consumer<? super T> action) {\n        orderedValues.forEach(action);\n    }\n\n    public Spliterator<T> spliterator() {\n        return orderedValues.spliterator();\n    }\n\n    @Nullable\n    public ResourceLocation getKey(T value) {\n        return backingMap.inverse().get(value);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/registry/Registries.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.registry;\n\nimport com.direwolf20.buildinggadgets.common.BuildingGadgets;\nimport com.direwolf20.buildinggadgets.common.tainted.building.tilesupport.ITileDataFactory;\nimport com.direwolf20.buildinggadgets.common.tainted.building.tilesupport.ITileDataSerializer;\nimport com.direwolf20.buildinggadgets.common.tainted.building.tilesupport.TileSupport;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.handle.IHandleProvider;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.handle.IObjectHandle;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.materials.objects.IUniqueObjectSerializer;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.materials.objects.UniqueItem;\nimport com.direwolf20.buildinggadgets.common.tainted.template.SerialisationSupport;\nimport com.direwolf20.buildinggadgets.common.util.ref.Reference;\nimport com.google.common.base.Preconditions;\nimport net.minecraft.core.Registry;\nimport net.minecraft.resources.ResourceKey;\nimport net.minecraft.resources.ResourceLocation;\nimport net.minecraftforge.common.capabilities.ICapabilityProvider;\nimport net.minecraftforge.fml.InterModComms;\nimport net.minecraftforge.fml.common.Mod.EventBusSubscriber;\nimport net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus;\nimport net.minecraftforge.registries.*;\n\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.function.Supplier;\n\n@EventBusSubscriber(modid = Reference.MODID, bus = Bus.MOD)\npublic final class Registries {\n    // Registry keys\n    public static final ResourceKey<Registry<ITileDataSerializer>> TILE_DATA_SERIALIZERS_KEY = ResourceKey.createRegistryKey(new ResourceLocation(Reference.MODID, \"tile_data/serializer\"));\n    public static final ResourceKey<Registry<IUniqueObjectSerializer>> UNIQUE_OBJECT_SERIALIZERS_KEY = ResourceKey.createRegistryKey(new ResourceLocation(Reference.MODID, \"unique_object/serializer\"));\n\n    // Actual thing we register too\n    public static final DeferredRegister<ITileDataSerializer> TILE_DATA_SERIALIZER_DEFERRED_REGISTER = DeferredRegister.create(TILE_DATA_SERIALIZERS_KEY, Reference.MODID);\n    public static final DeferredRegister<IUniqueObjectSerializer> UNIQUE_OBJECT_SERIALIZER_DEFERRED_REGISTER = DeferredRegister.create(UNIQUE_OBJECT_SERIALIZERS_KEY, Reference.MODID);\n\n    // Create the custom registries\n    public static final Supplier<IForgeRegistry<ITileDataSerializer>> TILE_DATA_SERIALIZER_REGISTRY = TILE_DATA_SERIALIZER_DEFERRED_REGISTER.makeRegistry(() -> new RegistryBuilder<ITileDataSerializer>().disableSaving().disableSync());\n    public static final Supplier<IForgeRegistry<IUniqueObjectSerializer>> UNIQUE_DATA_SERIALIZER_REGISTRY = UNIQUE_OBJECT_SERIALIZER_DEFERRED_REGISTER.makeRegistry(() -> new RegistryBuilder<IUniqueObjectSerializer>().disableSaving().disableSync());\n\n    // The things we're registering\n    public static final RegistryObject<ITileDataSerializer> NBT_TILE_DATA_SERIALIZER = TILE_DATA_SERIALIZER_DEFERRED_REGISTER.register(\"nbt_tile_data_serializer\", SerialisationSupport::createNbtSerializer);\n    public static final RegistryObject<ITileDataSerializer> DUMMY_TILE_DATA_SERIALIZER = TILE_DATA_SERIALIZER_DEFERRED_REGISTER.register(\"dummy_serializer\", SerialisationSupport::createDummySerializer);\n    public static final RegistryObject<IUniqueObjectSerializer> UNIQUE_OBJECT_SERIALIZER = UNIQUE_OBJECT_SERIALIZER_DEFERRED_REGISTER.register(\"simple_item\", UniqueItem.Serializer::new);\n\n    private Registries() {\n    }\n\n    private static TopologicalRegistryBuilder<ITileDataFactory> tileDataFactoryBuilder = TopologicalRegistryBuilder.create();\n    private static TopologicalRegistryBuilder<IHandleProvider> handleProviderBuilder = TopologicalRegistryBuilder.create();\n\n    private static ImmutableOrderedRegistry<ITileDataFactory> tileDataFactories = null;\n    private static ImmutableOrderedRegistry<IHandleProvider> handleProviders = null;\n\n    static {\n        addDefaultOrdered();\n    }\n\n    public static void createOrderedRegistries() {\n        BuildingGadgets.LOG.trace(\"Creating Ordered Registries\");\n        Preconditions.checkState(tileDataFactoryBuilder != null, \"Cannot create Ordered Registries twice!\");\n        tileDataFactories = tileDataFactoryBuilder.build();\n        tileDataFactoryBuilder = null;\n        handleProviders = handleProviderBuilder.build();\n        handleProviderBuilder = null;\n        BuildingGadgets.LOG.trace(\"Finished Creating Ordered Registries\");\n    }\n\n    public static boolean handleIMC(InterModComms.IMCMessage message) {\n        BuildingGadgets.LOG.debug(\"Received IMC message using Method {} from {}.\", message.getMethod(), message.getSenderModId());\n        if (message.method().equals(Reference.TileDataFactoryReference.IMC_METHOD_TILEDATA_FACTORY)) {\n            BuildingGadgets.LOG.debug(\"Recognized ITileDataFactory registration message. Registering.\");\n            Preconditions.checkState(tileDataFactoryBuilder != null,\n                    \"Attempted to register ITileDataFactory, after the Registry has been built!\");\n            TopologicalRegistryBuilder<ITileDataFactory> builder = message.<Supplier<TopologicalRegistryBuilder<ITileDataFactory>>>getMessageSupplier().get().get();\n            tileDataFactoryBuilder.merge(builder);\n            BuildingGadgets.LOG.trace(\"Registered {} from {} to the ITileDataFactory registry.\", builder, message.getSenderModId());\n            return true;\n        } else if (message.method().equals(Reference.HandleProviderReference.IMC_METHOD_HANDLE_PROVIDER)) {\n            BuildingGadgets.LOG.debug(\"Recognized IHandleProvider registration message. Registering.\");\n            Preconditions.checkState(handleProviderBuilder != null,\n                    \"Attempted to register IHandleProvider, after the Registry has been built!\");\n            TopologicalRegistryBuilder<IHandleProvider> builder = message.<Supplier<TopologicalRegistryBuilder<IHandleProvider>>>getMessageSupplier().get().get();\n            handleProviderBuilder.merge(builder);\n            BuildingGadgets.LOG.trace(\"Registered {} from {} to the IHandleProvider registry.\", builder, message.getSenderModId());\n            return true;\n        }\n        return false;\n    }\n\n    private static void addDefaultOrdered() {\n        tileDataFactoryBuilder\n                .addMarker(Reference.MARKER_BEFORE_RL)\n                .addMarker(Reference.MARKER_AFTER_RL)\n                .addValue(Reference.TileDataFactoryReference.DATA_PROVIDER_FACTORY_RL, TileSupport.dataProviderFactory())\n                .addDependency(Reference.MARKER_AFTER_RL, Reference.TileDataFactoryReference.DATA_PROVIDER_FACTORY_RL)\n                .addDependency(Reference.MARKER_BEFORE_RL, Reference.MARKER_AFTER_RL);\n        handleProviderBuilder\n                .addMarker(Reference.MARKER_BEFORE_RL)\n                .addMarker(Reference.MARKER_AFTER_RL)\n                .addDependency(Reference.MARKER_BEFORE_RL, Reference.MARKER_AFTER_RL);\n    }\n\n    public static final class TileEntityData {\n        private TileEntityData() {\n        }\n\n        public static ImmutableOrderedRegistry<ITileDataFactory> getTileDataFactories() {\n            Preconditions\n                    .checkState(tileDataFactories != null, \"Attempted to retrieve TileDataFactoryRegistry before it was created!\");\n            return tileDataFactories;\n        }\n    }\n\n    public static final class HandleProvider {\n        private HandleProvider() {\n        }\n\n        public static ImmutableOrderedRegistry<IHandleProvider> getHandleProviders() {\n            Preconditions\n                    .checkState(tileDataFactories != null, \"Attempted to retrieve HandleProviderRegistry before it was created!\");\n            return handleProviders;\n        }\n\n        public static boolean indexCapProvider(ICapabilityProvider provider, Map<Class<?>, Map<Object, List<IObjectHandle<?>>>> indexMap) {\n            Set<Class<?>> evaluatedClasses = new HashSet<>();\n            boolean indexed = false;\n            for (IHandleProvider handleProvider : getHandleProviders().getValuesInOrder()) {\n                indexed |= handleProvider.index(provider, indexMap, evaluatedClasses);\n            }\n            return indexed;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/registry/TopologicalRegistryBuilder.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.registry;\n\nimport com.google.common.base.MoreObjects;\nimport com.google.common.base.Preconditions;\nimport com.google.common.collect.ImmutableBiMap;\nimport com.google.common.collect.ImmutableList;\nimport com.google.common.graph.EndpointPair;\nimport com.google.common.graph.GraphBuilder;\nimport com.google.common.graph.MutableGraph;\nimport net.minecraft.resources.ResourceLocation;\nimport net.minecraftforge.fml.loading.toposort.TopologicalSort;\n\nimport javax.annotation.Nonnull;\nimport javax.annotation.Nullable;\nimport java.util.*;\n\npublic final class TopologicalRegistryBuilder<T> {\n    private final MutableGraph<ValueObject<T>> theGraph;\n    private final Map<ResourceLocation, ValueObject<T>> values;\n    private boolean build;\n\n    public static <T> TopologicalRegistryBuilder<T> create() {\n        return new TopologicalRegistryBuilder<>();\n    }\n\n    private TopologicalRegistryBuilder() {\n        this.theGraph = GraphBuilder.directed().allowsSelfLoops(false).build();\n        this.values = new TreeMap<>();\n        this.build = false;\n    }\n\n    public TopologicalRegistryBuilder<T> addAllValues(Map<ResourceLocation, T> values) {\n        for (Map.Entry<ResourceLocation, T> entry : values.entrySet()) {\n            addValue(entry.getKey(), entry.getValue());\n        }\n        return this;\n    }\n\n    public TopologicalRegistryBuilder<T> addValue(ResourceLocation key, T value) {\n        validateUnbuild();\n        ValueObject<T> obj;\n        Preconditions.checkArgument(! containsValue(value), \"Cannot have duplicate values, as the mapping needs to be bijective!\");\n        if (values.containsKey(Objects.requireNonNull(key))) {\n            obj = values.get(key);\n            obj.setValue(value);//override existing value\n        } else {\n            obj = new ValueObject<>(key, value);\n            values.put(key, obj);\n            theGraph.addNode(obj);\n        }\n        return this;\n    }\n\n    public TopologicalRegistryBuilder<T> addAllMarkers(Iterable<ResourceLocation> markers) {\n        for (ResourceLocation marker : markers) {\n            addMarker(marker);\n        }\n        return this;\n    }\n\n    public TopologicalRegistryBuilder<T> addMarker(ResourceLocation marker) {\n        validateUnbuild();\n        if (values.containsKey(Objects.requireNonNull(marker)))\n            return this;\n        ValueObject<T> obj = new ValueObject<>(marker, null);\n        values.put(marker, obj);\n        theGraph.addNode(obj);\n        return this;\n    }\n\n    public TopologicalRegistryBuilder<T> addDependency(ResourceLocation source, ResourceLocation dependent) {\n        validateUnbuild();\n        if (! values.containsKey(source))\n            addMarker(source);\n        if (! values.containsKey(dependent))\n            addMarker(dependent);\n        ValueObject<T> sourceObj = values.get(Objects.requireNonNull(source));\n        ValueObject<T> dependentObj = values.get(Objects.requireNonNull(dependent));\n        theGraph.putEdge(sourceObj, dependentObj);\n        return this;\n    }\n\n    public TopologicalRegistryBuilder<T> merge(TopologicalRegistryBuilder<T> other) {\n        validateUnbuild();\n        for (ValueObject<T> node : other.theGraph.nodes()) {\n            if (node.getValue() != null)\n                addValue(node.getKey(), node.getValue());//perform a copy... values are mutable...\n            else\n                addMarker(node.getKey());\n        }\n        for (EndpointPair<ValueObject<T>> edge : other.theGraph.edges()) {\n            addDependency(edge.source().getKey(), edge.target().getKey());\n        }\n        return this;\n    }\n\n    public ImmutableOrderedRegistry<T> build() {\n        validateUnbuild();\n        build = true;\n        values.clear();\n        List<ValueObject<T>> sorted = TopologicalSort.topologicalSort(theGraph, Comparator.naturalOrder());\n        final ImmutableList.Builder<T> objs = ImmutableList.builder();\n        final ImmutableBiMap.Builder<ResourceLocation, T> map = ImmutableBiMap.builder();\n        sorted.stream().filter(val -> val.getValue() != null).forEach(obj -> {\n            objs.add(obj.getValue());\n            map.put(obj.getKey(), obj.getValue());\n        });\n        return new ImmutableOrderedRegistry<>(map.build(), objs.build());\n    }\n\n    private void validateUnbuild() {\n        Preconditions.checkState(! build, \"Cannot access already created Builder!\");\n    }\n\n    @Override\n    public String toString() {\n        return MoreObjects.toStringHelper(this)\n                .add(\"values\", values.values())\n                .add(\"graph\", \"MutableGraph{\" + theGraph + \"}\")\n                .toString();\n    }\n\n    private boolean containsValue(T value) {\n        //needs iterating as we cannot use a BiMap here, because we cannot do a value check on ValueObject, as this would contradict equals transitivity\n        for (ValueObject<T> existing : values.values()) {\n            if (existing.getValue() == value || (existing.getValue() != null && existing.getValue().equals(value)))\n                return true;\n        }\n        return false;\n    }\n\n    private static final class ValueObject<T> implements Comparable<ValueObject<?>> {\n        @Nonnull\n        private final ResourceLocation key;\n        @Nullable\n        private T value;\n\n        private ValueObject(@Nonnull ResourceLocation key, @Nullable T value) {\n            this.key = key;\n            this.value = value;\n        }\n\n        @Nonnull\n        private ResourceLocation getKey() {\n            return key;\n        }\n\n        @Nullable\n        private T getValue() {\n            return value;\n        }\n\n        public void setValue(@Nonnull T value) {\n            this.value = Objects.requireNonNull(value);\n        }\n\n        @Override\n        public int compareTo(ValueObject<?> o) {\n            return getKey().compareTo(o.getKey());\n        }\n\n        @Override\n        public boolean equals(Object o) {\n            if (this == o) return true;\n            if (! (o instanceof ValueObject)) return false;\n            ValueObject<?> that = (ValueObject<?>) o;\n\n            return getKey().equals(that.getKey());\n        }\n\n        @Override\n        public int hashCode() {\n            return getKey().hashCode();\n        }\n\n        @Override\n        public String toString() {\n            return MoreObjects.toStringHelper(this)\n                    .omitNullValues()\n                    .add(\"key\", key)\n                    .add(\"value\", value)\n                    .toString();\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/registry/package-info.java",
    "content": "@ParametersAreNonnullByDefault\n@MethodsReturnNonnullByDefault\npackage com.direwolf20.buildinggadgets.common.tainted.registry;\n\nimport net.minecraft.MethodsReturnNonnullByDefault;\n\nimport javax.annotation.ParametersAreNonnullByDefault;"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/save/SaveManager.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.save;\n\nimport com.direwolf20.buildinggadgets.common.BuildingGadgets;\nimport com.direwolf20.buildinggadgets.common.util.ref.Reference.SaveReference;\nimport net.minecraft.server.level.ServerLevel;\nimport net.minecraft.world.level.Level;\nimport net.minecraftforge.event.server.ServerStartedEvent;\nimport net.minecraftforge.event.server.ServerStoppedEvent;\n\nimport javax.annotation.Nullable;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.function.Function;\nimport java.util.function.IntSupplier;\nimport java.util.function.Supplier;\n\npublic enum SaveManager {\n    INSTANCE;\n    private SaveTemplateProvider templateProvider;\n    private TemplateSave templateSave;\n    private List<UndoSaveContainer> undoSaves;\n\n    SaveManager() {\n        this.templateProvider = new SaveTemplateProvider(this::getTemplateSave);\n        this.undoSaves = new LinkedList<>();\n    }\n\n    public Supplier<UndoWorldSave> registerUndoSave(Function<ServerLevel, UndoWorldSave> ctrFun) {\n        UndoSaveContainer container = new UndoSaveContainer(Objects.requireNonNull(ctrFun));\n        this.undoSaves.add(container);\n        return container::getCurrentSave;\n    }\n\n    public void onServerStarted(ServerStartedEvent event) {\n        BuildingGadgets.LOG.debug(\"Loading World Saves.\");\n        ServerLevel world = event.getServer().getLevel(Level.OVERWORLD);\n        for (UndoSaveContainer c : undoSaves) {\n            c.acquire(world);\n        }\n        templateSave = getTemplateSave(world, SaveReference.TEMPLATE_SAVE_TEMPLATES);\n        BuildingGadgets.LOG.debug(\"Finished Loading saves\");\n    }\n\n    public void onServerStopped(ServerStoppedEvent event) {\n        BuildingGadgets.LOG.debug(\"Clearing save caches\");\n        for (UndoSaveContainer c : undoSaves) {\n            c.release();\n        }\n        templateSave = null;\n        BuildingGadgets.LOG.debug(\"Finished clearing save caches\");\n    }\n\n    public static UndoWorldSave getUndoSave(ServerLevel world, IntSupplier maxLengthSupplier, String name) {\n        return world.getDataStorage().computeIfAbsent(UndoWorldSave::loads, () -> new UndoWorldSave(maxLengthSupplier), name);\n    }\n\n    private static TemplateSave getTemplateSave(ServerLevel world, String name) {\n        return world.getDataStorage().computeIfAbsent(TemplateSave::loads, TemplateSave::new, name);\n    }\n\n//    private static <T extends SavedData> T get(ServerLevel world, Function<CompoundTag, T> loader, Supplier<T> supplier, String name) {\n//        return world.getDataStorage().computeIfAbsent(loader, supplier, name);\n//    }\n\n    public SaveTemplateProvider getTemplateProvider() {\n        return templateProvider;\n    }\n\n    public TemplateSave getTemplateSave() {\n        return templateSave;\n    }\n\n    private static final class UndoSaveContainer {\n        private final Function<ServerLevel, UndoWorldSave> constructor;\n        @Nullable\n        private UndoWorldSave currentSave;\n\n        private UndoSaveContainer(Function<ServerLevel, UndoWorldSave> constructor) {\n            this.constructor = constructor;\n            this.currentSave = null;\n        }\n\n        private void acquire(ServerLevel world) {\n            this.currentSave = constructor.apply(world);\n        }\n\n        @Nullable\n        private UndoWorldSave getCurrentSave() {\n            return currentSave;\n        }\n\n        private void release() {\n            currentSave = null;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/save/SaveTemplateProvider.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.save;\n\nimport com.direwolf20.buildinggadgets.common.BuildingGadgets;\nimport com.direwolf20.buildinggadgets.common.network.PacketHandler;\nimport com.direwolf20.buildinggadgets.common.network.packets.PacketRequestTemplate;\nimport com.direwolf20.buildinggadgets.common.network.packets.SplitPacketUpdateTemplate;\nimport com.direwolf20.buildinggadgets.common.tainted.template.ITemplateKey;\nimport com.direwolf20.buildinggadgets.common.tainted.template.ITemplateProvider;\nimport com.direwolf20.buildinggadgets.common.tainted.template.Template;\nimport net.minecraft.server.level.ServerPlayer;\nimport net.minecraftforge.network.PacketDistributor;\nimport org.apache.logging.log4j.util.TriConsumer;\n\nimport java.util.Collections;\nimport java.util.Set;\nimport java.util.UUID;\nimport java.util.WeakHashMap;\nimport java.util.function.Function;\nimport java.util.function.Supplier;\n\npublic final class SaveTemplateProvider implements ITemplateProvider {\n    private final Supplier<TemplateSave> save;\n    private final Set<IUpdateListener> updateListeners;\n\n    public SaveTemplateProvider(Supplier<TemplateSave> save) {\n        this.save = save;\n        this.updateListeners = Collections.newSetFromMap(new WeakHashMap<>());\n    }\n\n    public TemplateSave getSave() {\n        return save.get();\n    }\n\n    @Override\n    public Template getTemplateForKey(ITemplateKey key) {\n        UUID id = getId(key);\n        return getSave().getTemplate(id);\n    }\n\n    @Override\n    public void setTemplate(ITemplateKey key, Template template) {\n        getSave().setTemplate(key.getTemplateId(this::getFreeId), template);\n        notifyListeners(key, template, l -> l::onTemplateUpdate);\n    }\n\n    @Override\n    public boolean requestUpdate(ITemplateKey key) {\n        return false;\n    }\n\n    @Override\n    public boolean requestRemoteUpdate(ITemplateKey key) {\n        UUID id = getId(key);\n        Template template = getSave().getTemplate(id);\n        notifyListeners(key, template, l -> l::onTemplateUpdateSend);\n        PacketHandler.getSplitManager().send(new SplitPacketUpdateTemplate(id, template), PacketDistributor.ALL.noArg());\n        return true;\n    }\n\n    @Override\n    public void registerUpdateListener(IUpdateListener listener) {\n        updateListeners.add(listener);\n    }\n\n    @Override\n    public void removeUpdateListener(IUpdateListener listener) {\n        updateListeners.remove(listener);\n    }\n\n    @Override\n    public UUID getId(ITemplateKey key) {\n        return key.getTemplateId(this::getFreeId);\n    }\n\n    public boolean requestRemoteUpdate(ITemplateKey key, ServerPlayer playerEntity) {\n        return requestRemoteUpdate(key, PacketDistributor.PLAYER.with(() -> playerEntity));\n    }\n\n    @Override\n    public boolean requestRemoteUpdate(ITemplateKey key, PacketDistributor.PacketTarget target) {\n        UUID id = getId(key);\n        Template template = getSave().getTemplate(id);\n        PacketHandler.getSplitManager().send(new SplitPacketUpdateTemplate(id, template), target);\n        return true;\n    }\n\n    @Override\n    public boolean requestUpdate(ITemplateKey key, PacketDistributor.PacketTarget target) {\n        UUID id = getId(key);\n        PacketHandler.HANDLER.send(target, new PacketRequestTemplate(id));\n        return true;\n    }\n\n    public void onRemoteIdAllocated(UUID allocated) {\n        getSave().getTemplate(allocated);\n    }\n\n    private UUID getFreeId() {\n        UUID freeId = getSave().getFreeUUID();\n        return freeId;\n    }\n\n    private void notifyListeners(ITemplateKey key, Template template, Function<IUpdateListener, TriConsumer<ITemplateProvider, ITemplateKey, Template>> function) {\n        for (IUpdateListener listener : updateListeners) {\n            try {\n                function.apply(listener).accept(this, key, template);\n            } catch (Exception e) {\n                BuildingGadgets.LOG.error(\"Update listener threw an exception!\", e);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/save/TemplateSave.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.save;\n\nimport com.direwolf20.buildinggadgets.common.tainted.save.TemplateSave.TemplateInfo;\nimport com.direwolf20.buildinggadgets.common.tainted.template.Template;\nimport com.direwolf20.buildinggadgets.common.util.ref.NBTKeys;\nimport net.minecraft.nbt.CompoundTag;\n\nimport java.util.UUID;\n\npublic final class TemplateSave extends TimedDataSave<TemplateInfo> {\n\n    public TemplateSave() {\n        super();\n    }\n\n    public static TemplateSave loads(CompoundTag tag) {\n        TemplateSave templateSave = new TemplateSave();\n        templateSave.load(tag);\n        return templateSave;\n    }\n\n    public Template getTemplate(UUID id) {\n        TemplateInfo info = get(id, uuid -> new TemplateInfo());\n        return markDirtyAndUpdate(info).getTemplate();\n    }\n\n    void setTemplate(UUID id, Template template) {\n        markDirtyAndUpdate(get(id, uuid -> new TemplateInfo(template))).setTemplate(template);\n    }\n\n    void removeTemplate(UUID id) {\n        remove(id);\n        setDirty();\n    }\n\n    @Override\n    protected TemplateInfo createValue() {\n        return new TemplateInfo();\n    }\n\n    @Override\n    protected TemplateInfo readValue(CompoundTag nbt) {\n        return new TemplateInfo(nbt);\n    }\n\n    private TemplateInfo markDirtyAndUpdate(TemplateInfo info) {\n        setDirty();\n        return info.updateTime();\n    }\n\n    static final class TemplateInfo extends TimedDataSave.TimedValue { //for reasons I don't understand it doesn't compile if you leave the TimedDataSave out!\n        private Template template;\n\n        private TemplateInfo(CompoundTag nbt) {\n            super(nbt);\n            template = Template.deserialize(nbt.getCompound(NBTKeys.KEY_DATA), null, true);\n        }\n\n        private TemplateInfo(Template template) {\n            super();\n            this.template = template;\n        }\n\n        private TemplateInfo(long lastUpdateTime, Template template) {\n            super(lastUpdateTime);\n            this.template = template;\n        }\n\n        private TemplateInfo() {\n            this(new Template());\n        }\n\n        private Template getTemplate() {\n            return template;\n        }\n\n        public TemplateInfo setTemplate(Template template) {\n            this.template = template;\n            return this;\n        }\n\n        @Override\n        public TemplateInfo updateTime() {\n            return (TemplateInfo) super.updateTime();\n        }\n\n        @Override\n        public CompoundTag write() {\n            CompoundTag nbt = super.write();\n            nbt.put(NBTKeys.KEY_DATA, template.serialize(true));\n            return nbt;\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/save/TimedDataSave.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.save;\n\nimport com.direwolf20.buildinggadgets.common.tainted.save.TimedDataSave.TimedValue;\nimport com.direwolf20.buildinggadgets.common.util.helpers.NBTHelper;\nimport com.direwolf20.buildinggadgets.common.util.ref.NBTKeys;\nimport it.unimi.dsi.fastutil.longs.Long2ObjectRBTreeMap;\nimport it.unimi.dsi.fastutil.longs.Long2ObjectSortedMap;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.nbt.Tag;\nimport net.minecraft.nbt.ListTag;\nimport net.minecraft.network.FriendlyByteBuf;\nimport net.minecraft.world.level.saveddata.SavedData;\n\nimport java.util.*;\nimport java.util.function.Function;\n\npublic abstract class TimedDataSave<T extends TimedValue> extends SavedData {\n    private Map<UUID, T> idToValue;\n    private Long2ObjectSortedMap<Set<UUID>> timeToId;\n\n    public TimedDataSave() {\n        super();\n        this.idToValue = new HashMap<>();\n        this.timeToId = new Long2ObjectRBTreeMap<>();\n    }\n\n    public UUID getFreeUUID() {\n        UUID res = UUID.randomUUID();\n        return idToValue.containsKey(res) ? getFreeUUID() : res;\n    }\n\n    protected void writeAllIds(FriendlyByteBuf buffer) {\n        for (UUID id : idToValue.keySet()) {\n            buffer.writeUUID(id);\n        }\n    }\n\n    protected T get(UUID id) {\n        return get(id, uuid -> createValue());\n    }\n\n    protected T get(UUID id, Function<UUID, T> factory) {\n        setDirty();\n        return idToValue.computeIfAbsent(id, factory);\n    }\n\n    protected void remove(UUID id) {\n        T val = idToValue.remove(id);\n        if (val != null) {\n            Set<UUID> set = timeToId.get(val.getUpdateTime());\n            if (! set.isEmpty()) {\n                set.remove(id);\n                if (set.isEmpty())\n                    timeToId.remove(val.getUpdateTime());\n            }\n        }\n    }\n\n    protected boolean contains(UUID id) {\n        return idToValue.containsKey(id);\n    }\n\n    public long getLastUpdateTime(UUID id) {\n        return get(id).getUpdateTime();\n    }\n\n    public void load(CompoundTag nbt) {\n        Tag timeList = nbt.get(NBTKeys.WORD_SAVE_DATA_MAP);\n        timeToId.clear();\n        idToValue.clear();\n        if (timeList instanceof ListTag) {\n            NBTHelper.deserializeUUIDMap((ListTag) timeList, idToValue, inbt -> readValue((CompoundTag) inbt));\n            for (Map.Entry<UUID, T> entry : idToValue.entrySet()) {\n                timeToId.computeIfAbsent(entry.getValue().getUpdateTime(), i -> new HashSet<>()).add(entry.getKey());\n            }\n        }\n    }\n\n    @Override\n    public CompoundTag save(CompoundTag compound) {\n        ListTag data = NBTHelper.serializeUUIDMap(idToValue, TimedValue::write);\n        compound.put(NBTKeys.WORD_SAVE_DATA_MAP, data);\n        return compound;\n    }\n\n    protected abstract T createValue();\n\n    protected abstract T readValue(CompoundTag nbt);\n\n    public static class TimedValue {\n        private long lastUpdateTime;\n\n        protected TimedValue(CompoundTag nbt) {\n            this(nbt.contains(NBTKeys.WORLD_SAVE_TIME, Tag.TAG_LONG) ? nbt.getLong(NBTKeys.WORLD_SAVE_TIME) : System.currentTimeMillis());\n        }\n\n        protected TimedValue(long lastUpdateTime) {\n            this.lastUpdateTime = lastUpdateTime;\n        }\n\n        protected TimedValue() {\n            this(System.currentTimeMillis());\n        }\n\n        public TimedValue updateTime() {\n            lastUpdateTime = System.currentTimeMillis();\n            return this;\n        }\n\n        public long getUpdateTime() {\n            return lastUpdateTime;\n        }\n\n        public CompoundTag write() {\n            CompoundTag nbt = new CompoundTag();\n            nbt.putLong(NBTKeys.WORLD_SAVE_TIME, lastUpdateTime);\n            return nbt;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/save/Undo.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.save;\n\nimport com.direwolf20.buildinggadgets.common.BuildingGadgets;\nimport com.direwolf20.buildinggadgets.common.tainted.building.BlockData;\nimport com.direwolf20.buildinggadgets.common.tainted.building.Region;\nimport com.direwolf20.buildinggadgets.common.tainted.building.tilesupport.ITileDataSerializer;\nimport com.direwolf20.buildinggadgets.common.tainted.building.tilesupport.ITileEntityData;\nimport com.direwolf20.buildinggadgets.common.tainted.building.tilesupport.NBTTileEntityData;\nimport com.direwolf20.buildinggadgets.common.tainted.building.tilesupport.TileSupport;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.materials.objects.IUniqueObject;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.materials.objects.IUniqueObjectSerializer;\nimport com.direwolf20.buildinggadgets.common.tainted.registry.Registries;\nimport com.direwolf20.buildinggadgets.common.tainted.template.SerialisationSupport;\nimport com.direwolf20.buildinggadgets.common.util.compression.DataCompressor;\nimport com.direwolf20.buildinggadgets.common.util.compression.DataDecompressor;\nimport com.direwolf20.buildinggadgets.common.util.helpers.NBTHelper;\nimport com.direwolf20.buildinggadgets.common.util.ref.NBTKeys;\nimport com.direwolf20.buildinggadgets.common.util.tools.RegistryUtils;\nimport com.google.common.base.Preconditions;\nimport com.google.common.collect.HashMultiset;\nimport com.google.common.collect.ImmutableMap;\nimport com.google.common.collect.Multiset;\nimport com.google.common.collect.Multiset.Entry;\nimport com.google.common.collect.Multisets;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.nbt.*;\nimport net.minecraft.resources.ResourceKey;\nimport net.minecraft.resources.ResourceLocation;\nimport net.minecraft.util.Tuple;\nimport net.minecraft.world.level.BlockGetter;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.entity.BlockEntity;\nimport net.minecraft.world.level.block.state.BlockState;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.function.IntFunction;\nimport java.util.function.ToIntFunction;\n\npublic final class Undo {\n    static Undo deserialize(CompoundTag nbt) {\n        Preconditions.checkArgument(nbt.contains(NBTKeys.WORLD_SAVE_DIM, Tag.TAG_STRING)\n                && nbt.contains(NBTKeys.WORLD_SAVE_UNDO_ITEMS_SERIALIZER_LIST, Tag.TAG_LIST)\n                && nbt.contains(NBTKeys.WORLD_SAVE_UNDO_BLOCK_LIST, Tag.TAG_LIST)\n                && nbt.contains(NBTKeys.WORLD_SAVE_UNDO_DATA_LIST, Tag.TAG_LIST)\n                && nbt.contains(NBTKeys.WORLD_SAVE_UNDO_DATA_SERIALIZER_LIST, Tag.TAG_LIST));\n        DataDecompressor<ITileDataSerializer> serializerReverseObjectIncrementer = new DataDecompressor<>(\n                (ListTag) nbt.get(NBTKeys.WORLD_SAVE_UNDO_DATA_SERIALIZER_LIST),\n                inbt -> {\n                    String s = inbt.getAsString();\n                    ITileDataSerializer serializer = RegistryUtils.getFromString(Registries.TILE_DATA_SERIALIZER_REGISTRY.get(), s);\n                    if (serializer == null) {\n                        BuildingGadgets.LOG.warn(\"Found unknown serializer {}. Replacing with dummy!\", s);\n                        serializer = TileSupport.dummyTileEntityData().getSerializer();\n                    }\n                    return serializer;\n                },\n                value -> {\n                    BuildingGadgets.LOG.warn(\"Attempted to query unknown serializer {}. Replacing with dummy!\", value);\n                    return TileSupport.dummyTileEntityData().getSerializer();\n                });\n        DataDecompressor<BlockData> dataReverseObjectIncrementer = new DataDecompressor<>(\n                (ListTag) nbt.get(NBTKeys.WORLD_SAVE_UNDO_DATA_LIST),\n                inbt -> BlockData.deserialize((CompoundTag) inbt, serializerReverseObjectIncrementer, true),\n                value -> BlockData.AIR);\n        DataDecompressor<IUniqueObjectSerializer> itemSerializerIncrementer = new DataDecompressor<>(\n                (ListTag) nbt.get(NBTKeys.WORLD_SAVE_UNDO_ITEMS_SERIALIZER_LIST),\n                inbt -> {\n                    String s = inbt.getAsString();\n                    IUniqueObjectSerializer serializer = RegistryUtils.getFromString(Registries.UNIQUE_DATA_SERIALIZER_REGISTRY.get(), s);\n                    if (serializer == null)\n                        return SerialisationSupport.uniqueItemSerializer();\n                    return serializer;\n                },\n                value -> {\n                    BuildingGadgets.LOG.warn(\"Attempted to query unknown item-serializer {}. Replacing with default!\", value);\n                    return SerialisationSupport.uniqueItemSerializer();\n                });\n        DataDecompressor<Multiset<IUniqueObject<?>>> itemSetReverseObjectIncrementer = new DataDecompressor<>(\n                (ListTag) nbt.get(NBTKeys.WORLD_SAVE_UNDO_ITEMS_LIST),\n                inbt -> NBTHelper.deserializeMultisetEntries((ListTag) inbt, HashMultiset.create(), entry -> readEntry(entry, itemSerializerIncrementer)),\n                value -> HashMultiset.create());\n        Map<BlockPos, BlockInfo> map = NBTHelper.deserializeMap(\n                (ListTag) nbt.get(NBTKeys.WORLD_SAVE_UNDO_BLOCK_LIST), new HashMap<>(),\n                inbt -> NbtUtils.readBlockPos((CompoundTag) inbt),\n                inbt -> BlockInfo.deserialize((CompoundTag) inbt, dataReverseObjectIncrementer, itemSetReverseObjectIncrementer));\n\n        ResourceKey<Level> dim = ResourceKey.create(net.minecraft.core.registries.Registries.DIMENSION, new ResourceLocation(nbt.getString(NBTKeys.WORLD_SAVE_DIM)));\n        Region bounds = Region.deserializeFrom(nbt.getCompound(NBTKeys.WORLD_SAVE_UNDO_BOUNDS));\n        return new Undo(dim, map, bounds);\n    }\n\n    private static Tuple<IUniqueObject<?>, Integer> readEntry(Tag inbt, IntFunction<IUniqueObjectSerializer> serializerIntFunction) {\n        CompoundTag nbt = (CompoundTag) inbt;\n        IUniqueObjectSerializer serializer = serializerIntFunction.apply(nbt.getInt(NBTKeys.UNIQUE_ITEM_SERIALIZER));\n        int count = nbt.getInt(NBTKeys.UNIQUE_ITEM_COUNT);\n        IUniqueObject<?> item = serializer.deserialize(nbt.getCompound(NBTKeys.UNIQUE_ITEM_ITEM));\n        return new Tuple<>(item, count);\n    }\n\n    public static Builder builder() {\n        return new Builder();\n    }\n\n    private ResourceKey<Level> dim;\n    private Map<BlockPos, BlockInfo> dataMap;\n    private Region boundingBox;\n\n    public Undo(ResourceKey<Level> dim, Map<BlockPos, BlockInfo> dataMap, Region boundingBox) {\n        this.dim = dim;\n        this.dataMap = dataMap;\n        this.boundingBox = boundingBox;\n    }\n\n    public Region getBoundingBox() {\n        return boundingBox;\n    }\n\n    public Map<BlockPos, BlockInfo> getUndoData() {\n        return Collections.unmodifiableMap(dataMap);\n    }\n\n    CompoundTag serialize() {\n        DataCompressor<BlockData> dataObjectIncrementer = new DataCompressor<>();\n        DataCompressor<IUniqueObjectSerializer> itemSerializerIncrementer = new DataCompressor<>();\n        DataCompressor<Multiset<IUniqueObject<?>>> itemObjectIncrementer = new DataCompressor<>();\n        DataCompressor<ITileDataSerializer> serializerObjectIncrementer = new DataCompressor<>();\n        CompoundTag res = new CompoundTag();\n\n        ListTag infoList = NBTHelper.serializeMap(dataMap, NbtUtils::writeBlockPos, i -> i.serialize(dataObjectIncrementer, itemObjectIncrementer));\n        ListTag dataList = dataObjectIncrementer.write(d -> d.serialize(serializerObjectIncrementer, true));\n        ListTag itemSetList = itemObjectIncrementer.write(ms -> NBTHelper.writeIterable(ms.entrySet(), entry -> writeEntry(entry, itemSerializerIncrementer)));\n        ListTag dataSerializerList = serializerObjectIncrementer.write(ts -> StringTag.valueOf(Registries.TILE_DATA_SERIALIZER_REGISTRY.get().getKey(ts).toString()));\n        ListTag itemSerializerList = itemSerializerIncrementer.write(s -> StringTag.valueOf(Registries.UNIQUE_DATA_SERIALIZER_REGISTRY.get().getKey(s).toString()));\n\n        res.putString(NBTKeys.WORLD_SAVE_DIM, dim.location().toString());\n        res.put(NBTKeys.WORLD_SAVE_UNDO_BLOCK_LIST, infoList);\n        res.put(NBTKeys.WORLD_SAVE_UNDO_DATA_LIST, dataList);\n        res.put(NBTKeys.WORLD_SAVE_UNDO_DATA_SERIALIZER_LIST, dataSerializerList);\n        res.put(NBTKeys.WORLD_SAVE_UNDO_ITEMS_LIST, itemSetList);\n        res.put(NBTKeys.WORLD_SAVE_UNDO_ITEMS_SERIALIZER_LIST, itemSerializerList);\n        res.put(NBTKeys.WORLD_SAVE_UNDO_BOUNDS, boundingBox.serialize());\n\n        return res;\n    }\n\n    private CompoundTag writeEntry(Entry<IUniqueObject<?>> entry, ToIntFunction<IUniqueObjectSerializer> serializerObjectIncrementer) {\n        CompoundTag res = new CompoundTag();\n        res.putInt(NBTKeys.UNIQUE_ITEM_SERIALIZER, serializerObjectIncrementer.applyAsInt(entry.getElement().getSerializer()));\n        res.put(NBTKeys.UNIQUE_ITEM_ITEM, entry.getElement().getSerializer().serialize(entry.getElement(), true));\n        res.putInt(NBTKeys.UNIQUE_ITEM_COUNT, entry.getCount());\n        return res;\n    }\n\n    public static final class BlockInfo {\n        private static BlockInfo deserialize(CompoundTag nbt, IntFunction<BlockData> dataSupplier, IntFunction<Multiset<IUniqueObject<?>>> itemSetSupplier) {\n            BlockData data = dataSupplier.apply(nbt.getInt(NBTKeys.WORLD_SAVE_UNDO_RECORDED_DATA));\n            BlockData placedData = dataSupplier.apply(nbt.getInt(NBTKeys.WORLD_SAVE_UNDO_PLACED_DATA));\n            Multiset<IUniqueObject<?>> usedItems = itemSetSupplier.apply(nbt.getInt(NBTKeys.WORLD_SAVE_UNDO_ITEMS_USED));\n            Multiset<IUniqueObject<?>> producedItems = itemSetSupplier.apply(nbt.getInt(NBTKeys.WORLD_SAVE_UNDO_ITEMS_PRODUCED));\n            return new BlockInfo(data, placedData, usedItems, producedItems);\n        }\n\n        private final BlockData recordedData;\n        private final BlockData placedData;\n        private final Multiset<IUniqueObject<?>> usedItems;\n        private final Multiset<IUniqueObject<?>> producedItems;\n\n        private BlockInfo(BlockData recordedData, BlockData placedData, Multiset<IUniqueObject<?>> usedItems, Multiset<IUniqueObject<?>> producedItems) {\n            this.recordedData = recordedData;\n            this.placedData = placedData;\n            this.usedItems = usedItems;\n            this.producedItems = producedItems;\n        }\n\n        private CompoundTag serialize(ToIntFunction<BlockData> dataIdSupplier, ToIntFunction<Multiset<IUniqueObject<?>>> itemIdSupplier) {\n            CompoundTag res = new CompoundTag();\n            res.putInt(NBTKeys.WORLD_SAVE_UNDO_RECORDED_DATA, dataIdSupplier.applyAsInt(recordedData));\n            res.putInt(NBTKeys.WORLD_SAVE_UNDO_PLACED_DATA, dataIdSupplier.applyAsInt(placedData));\n            res.putInt(NBTKeys.WORLD_SAVE_UNDO_ITEMS_USED, itemIdSupplier.applyAsInt(usedItems));\n            res.putInt(NBTKeys.WORLD_SAVE_UNDO_ITEMS_PRODUCED, itemIdSupplier.applyAsInt(producedItems));\n            return res;\n        }\n\n        public BlockData getRecordedData() {\n            return recordedData;\n        }\n\n        public BlockData getPlacedData() {\n            return placedData;\n        }\n\n        public Multiset<IUniqueObject<?>> getUsedItems() {\n            return Multisets.unmodifiableMultiset(usedItems);\n        }\n\n        public Multiset<IUniqueObject<?>> getProducedItems() {\n            return Multisets.unmodifiableMultiset(producedItems);\n        }\n    }\n\n    public static final class Builder {\n        private final ImmutableMap.Builder<BlockPos, BlockInfo> mapBuilder;\n        private Region.Builder regionBuilder;\n\n        private Builder() {\n            mapBuilder = ImmutableMap.builder();\n            regionBuilder = null;\n        }\n\n        public Builder record(BlockGetter reader, BlockPos pos, BlockData placeData, Multiset<IUniqueObject<?>> requiredItems, Multiset<IUniqueObject<?>> producedItems) {\n            BlockState state = reader.getBlockState(pos);\n            BlockEntity te = reader.getBlockEntity(pos);\n            ITileEntityData data = te != null ? NBTTileEntityData.ofTile(te) : TileSupport.dummyTileEntityData();\n            return record(pos, new BlockData(state, data), placeData, requiredItems, producedItems);\n        }\n\n        private Builder record(BlockPos pos, BlockData recordedData, BlockData placedData, Multiset<IUniqueObject<?>> requiredItems, Multiset<IUniqueObject<?>> producedItems) {\n            mapBuilder.put(pos, new BlockInfo(recordedData, placedData, requiredItems, producedItems));\n            if (regionBuilder == null)\n                regionBuilder = Region.enclosingBuilder();\n            regionBuilder.enclose(pos);\n            return this;\n        }\n\n        public Undo build(Level dim) {\n            return new Undo(dim.dimension(), mapBuilder.build(), regionBuilder != null ? regionBuilder.build() : Region.singleZero());\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/save/UndoHistory.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.save;\n\nimport com.direwolf20.buildinggadgets.common.util.helpers.NBTHelper;\nimport com.direwolf20.buildinggadgets.common.util.ref.NBTKeys;\nimport com.google.common.base.Preconditions;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.nbt.Tag;\nimport net.minecraft.nbt.ListTag;\n\nimport java.util.Deque;\nimport java.util.LinkedList;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.function.IntSupplier;\n\nfinal class UndoHistory {\n    private final Deque<Undo> history;\n    private final IntSupplier maxLengthSupplier;\n\n    UndoHistory(IntSupplier maxLengthSupplier) {\n        this.maxLengthSupplier = Objects.requireNonNull(maxLengthSupplier);\n        this.history = new LinkedList<>();\n    }\n\n    UndoHistory add(Undo undo) {\n        history.addFirst(undo);\n        ensureSize();\n        return this;\n    }\n\n    Optional<Undo> get() {\n        ensureSize();\n        return Optional.ofNullable(history.pollFirst());\n    }\n\n    Optional<Undo> peek() {\n        ensureSize();\n        return Optional.ofNullable(history.peekFirst());\n    }\n\n    void read(CompoundTag nbt) {\n        this.history.clear();\n        Tag list = nbt.get(NBTKeys.WORLD_SAVE_UNDO_HISTORY);\n        if (list instanceof ListTag) {\n            NBTHelper.deserializeCollection((ListTag) list, history, inbt -> Undo.deserialize((CompoundTag) inbt));\n            ensureSize();\n        }\n    }\n\n    public void write(CompoundTag nbt) {\n        nbt.put(NBTKeys.WORLD_SAVE_UNDO_HISTORY, NBTHelper.writeIterable(history, Undo::serialize));\n    }\n\n    private void ensureSize() {\n        int maxLength = maxLengthSupplier.getAsInt();\n        Preconditions.checkArgument(maxLength >= 0, \"Cannot have a negative max History Length!!!\");\n        while (history.size() > maxLength) //max-length is non-negative => this always terminates\n            history.pollLast();\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/save/UndoWorldSave.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.save;\n\nimport com.direwolf20.buildinggadgets.common.tainted.save.UndoWorldSave.UndoValue;\nimport net.minecraft.nbt.CompoundTag;\n\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.UUID;\nimport java.util.function.IntSupplier;\n\npublic class UndoWorldSave extends TimedDataSave<UndoValue> {\n    private final IntSupplier undoMaxLength;\n\n    public UndoWorldSave(IntSupplier undoMaxLength) {\n        super();\n        this.undoMaxLength = Objects.requireNonNull(undoMaxLength);\n    }\n\n    public static UndoWorldSave loads(CompoundTag tag) {\n        UndoWorldSave undoWorldSave = new UndoWorldSave(() -> tag.getInt(\"maxUndo\"));\n        undoWorldSave.load(tag);\n        return undoWorldSave;\n    }\n\n    @Override\n    public CompoundTag save(CompoundTag compound) {\n        CompoundTag save = super.save(compound);\n        save.putInt(\"maxUndo\", this.undoMaxLength.getAsInt());\n        return save;\n    }\n\n    public void insertUndo(UUID uuid, Undo undo) {\n        UndoValue val = getAndUpdateTime(uuid);\n        val.getHistory().add(undo);\n    }\n\n    public Optional<Undo> getUndo(UUID uuid) {\n        return getAndUpdateTime(uuid).getHistory().get();\n    }\n\n    public Optional<Undo> peekSnapshot(UUID uuid) {\n        return getAndUpdateTime(uuid).getHistory().peek();\n    }\n\n    public void removeHistory(UUID uuid) {\n        remove(uuid);\n    }\n\n    private UndoValue getAndUpdateTime(UUID uuid) {\n        UndoValue val = get(uuid);\n        val.updateTime();\n        return val;\n    }\n\n    @Override\n    protected UndoValue createValue() {\n        return new UndoValue(undoMaxLength);\n    }\n\n    @Override\n    protected UndoValue readValue(CompoundTag nbt) {\n        return new UndoValue(nbt, undoMaxLength);\n    }\n\n    static final class UndoValue extends TimedDataSave.TimedValue { //for reasons I don't understand it doesn't compile if you leave the TimedDataSave out!\n        private final UndoHistory history;\n\n        private UndoValue(CompoundTag nbt, IntSupplier supplier) {\n            super(nbt);\n            this.history = new UndoHistory(supplier);\n            history.read(nbt);\n        }\n\n        private UndoValue(IntSupplier maxLength) {\n            super();\n            this.history = new UndoHistory(maxLength);\n        }\n\n        private UndoHistory getHistory() {\n            return history;\n        }\n\n        @Override\n        public CompoundTag write() {\n            CompoundTag nbt = super.write();\n            history.write(nbt);\n            return nbt;\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/template/ITemplateKey.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.template;\n\nimport java.util.UUID;\nimport java.util.function.Supplier;\n\npublic interface ITemplateKey {\n    UUID getTemplateId(Supplier<UUID> freeIdAllocator);\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/template/ITemplateProvider.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.template;\n\nimport com.direwolf20.buildinggadgets.common.capability.CapabilityTemplate;\nimport net.minecraftforge.common.capabilities.ICapabilityProvider;\nimport net.minecraftforge.common.util.NonNullSupplier;\nimport net.minecraftforge.network.PacketDistributor;\n\nimport java.util.UUID;\n\npublic interface ITemplateProvider {\n    UUID getId(ITemplateKey key);\n\n    Template getTemplateForKey(ITemplateKey key);\n\n    default <T extends Throwable> Template getTemplateForKey(ICapabilityProvider provider, NonNullSupplier<? extends T> exceptionSupplier) throws T {\n        return getTemplateForKey(provider.getCapability(CapabilityTemplate.TEMPLATE_KEY_CAPABILITY).orElseThrow(exceptionSupplier));\n    }\n\n    /**\n     * Overrides the TemplateItem for the given key.\n     * <p>\n     * Please prefer using TemplateTransactions and only use this if you absolutely have to (f.e. this is used for syncing).\n     * This is, because if you use this Method and someone else is running a Transaction on the previous value associated with key\n     * the result will silently not show up!\n     *\n     * @param key      The key for which the TemplateItem should be set\n     * @param template The TemplateItem to set\n     */\n    void setTemplate(ITemplateKey key, Template template);\n\n    /**\n     * Requests an update <b>from</b> the other side - aka requests the other side to send an update. Has <b>no effect on Servers</b> as,\n     * the Client from which to request the update from would be undefined.\n     *\n     * @param key The {@link ITemplateKey} for which to request an update\n     * @return whether or not an update was requested\n     */\n    boolean requestUpdate(ITemplateKey key);\n\n    /**\n     * Requests an update from the specified target.\n     *\n     * @param target The target to which to request the update\n     * @see #requestUpdate(ITemplateKey)\n     */\n    boolean requestUpdate(ITemplateKey key, PacketDistributor.PacketTarget target);\n\n    /**\n     * Requests an update <b>for<b/> the other side - aka sends an update packet to it. On the client this will send the data to the server,\n     * on the server this will send the data to <b>all logged in Clients</b>.\n     * @param key The key to request a remote update for\n     * @return whether or not a remote update was requested.\n     */\n    boolean requestRemoteUpdate(ITemplateKey key);\n\n    /**\n     * Requests a remote update for the specified target.\n     *\n     * @param target The target for which to request an update\n     * @see #requestRemoteUpdate(ITemplateKey)\n     */\n    boolean requestRemoteUpdate(ITemplateKey key, PacketDistributor.PacketTarget target);\n\n\n    /**\n     * Registers an Update Listener - it will only be weakly referenced!\n     */\n    void registerUpdateListener(IUpdateListener listener);\n\n    void removeUpdateListener(IUpdateListener listener);\n\n    interface IUpdateListener {\n        default void onTemplateUpdate(ITemplateProvider provider, ITemplateKey key, Template template) {\n\n        }\n\n        default void onTemplateUpdateSend(ITemplateProvider provider, ITemplateKey key, Template template) {\n\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/template/InMemoryTemplateProvider.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.template;\n\n\nimport net.minecraftforge.network.PacketDistributor;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.UUID;\n\n\npublic class InMemoryTemplateProvider implements ITemplateProvider {\n    private final Map<UUID, Template> map;\n\n    public InMemoryTemplateProvider() {\n        this.map = new HashMap<>();\n    }\n\n    @Override\n    public Template getTemplateForKey(ITemplateKey key) {\n        return map.computeIfAbsent(getId(key), id -> new Template());\n    }\n\n    @Override\n    public UUID getId(ITemplateKey key) {\n        return key.getTemplateId(this::requestFreeId);\n    }\n\n    @Override\n    public boolean requestUpdate(ITemplateKey key) {\n        return false;\n    }\n\n    @Override\n    public boolean requestRemoteUpdate(ITemplateKey key) {\n        return false;\n    }\n\n    @Override\n    public boolean requestUpdate(ITemplateKey key, PacketDistributor.PacketTarget target) {\n        return false;\n    }\n\n    @Override\n    public boolean requestRemoteUpdate(ITemplateKey key, PacketDistributor.PacketTarget target) {\n        return false;\n    }\n\n    @Override\n    public void setTemplate(ITemplateKey key, Template template) {\n        this.map.put(key.getTemplateId(this::requestFreeId), template);\n    }\n\n    public void clear() {\n        this.map.clear();\n    }\n\n    private UUID requestFreeId() {\n        UUID res = UUID.randomUUID();\n        return map.containsKey(res) ? requestFreeId() : res;\n    }\n\n    @Override\n    public void registerUpdateListener(IUpdateListener listener) {\n\n    }\n\n    @Override\n    public void removeUpdateListener(IUpdateListener listener) {\n\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/template/SerialisationSupport.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.template;\n\nimport com.direwolf20.buildinggadgets.common.tainted.building.tilesupport.ITileDataSerializer;\nimport com.direwolf20.buildinggadgets.common.tainted.building.tilesupport.ITileEntityData;\nimport com.direwolf20.buildinggadgets.common.tainted.building.tilesupport.NBTTileEntityData;\nimport com.direwolf20.buildinggadgets.common.tainted.building.tilesupport.TileSupport;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.materials.MaterialList;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.materials.objects.IUniqueObjectSerializer;\nimport com.direwolf20.buildinggadgets.common.tainted.registry.Registries;\nimport com.direwolf20.buildinggadgets.common.util.ref.NBTKeys;\nimport com.google.common.base.Preconditions;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.nbt.Tag;\n\npublic final class SerialisationSupport {\n    private SerialisationSupport() {\n    }\n\n    public static ITileDataSerializer dummyDataSerializer() {\n        return Registries.DUMMY_TILE_DATA_SERIALIZER.get();\n    }\n\n    public static final class DummyTileDataSerializer implements ITileDataSerializer {\n\n        private DummyTileDataSerializer() {\n            super();\n        }\n\n        @Override\n        public CompoundTag serialize(ITileEntityData data, boolean persisted) {\n            return new CompoundTag();\n        }\n\n        @Override\n        public ITileEntityData deserialize(CompoundTag tagCompound, boolean persisted) {\n            return TileSupport.dummyTileEntityData();\n        }\n    }\n\n    public static NBTTileEntityDataSerializer createNbtSerializer() {\n        return new NBTTileEntityDataSerializer();\n    }\n\n    public static DummyTileDataSerializer createDummySerializer() {\n        return new DummyTileDataSerializer();\n    }\n\n    public static final class NBTTileEntityDataSerializer implements ITileDataSerializer {\n        private NBTTileEntityDataSerializer() {\n        }\n\n\n        @Override\n        public CompoundTag serialize(ITileEntityData data, boolean persisted) {\n            Preconditions.checkArgument(data instanceof NBTTileEntityData);\n            NBTTileEntityData nbtData = (NBTTileEntityData) data;\n            CompoundTag res = new CompoundTag();\n            res.put(NBTKeys.KEY_DATA, nbtData.getNBT());\n            if (nbtData.getRequiredMaterials() != null)\n                res.put(NBTKeys.KEY_MATERIALS, nbtData.getRequiredMaterials().serialize(persisted));\n            return res;\n        }\n\n        @Override\n        public ITileEntityData deserialize(CompoundTag tagCompound, boolean persisted) {\n            CompoundTag data = tagCompound.getCompound(NBTKeys.KEY_DATA);\n            MaterialList materialList = null;\n            if (tagCompound.contains(NBTKeys.KEY_MATERIALS, Tag.TAG_COMPOUND))\n                materialList = MaterialList.deserialize(tagCompound.getCompound(NBTKeys.KEY_MATERIALS), persisted);\n            return new NBTTileEntityData(data, materialList);\n        }\n    }\n\n    public static IUniqueObjectSerializer uniqueItemSerializer() {\n        return Registries.UNIQUE_OBJECT_SERIALIZER.get();\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/template/Template.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.template;\n\nimport com.direwolf20.buildinggadgets.common.tainted.building.BlockData;\nimport com.direwolf20.buildinggadgets.common.tainted.building.Region;\nimport com.direwolf20.buildinggadgets.common.tainted.building.tilesupport.ITileDataSerializer;\nimport com.direwolf20.buildinggadgets.common.tainted.building.view.BuildContext;\nimport com.direwolf20.buildinggadgets.common.tainted.building.view.IBuildView;\nimport com.direwolf20.buildinggadgets.common.tainted.building.view.PositionalBuildView;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.materials.MaterialList;\nimport com.direwolf20.buildinggadgets.common.tainted.registry.Registries;\nimport com.direwolf20.buildinggadgets.common.util.CommonUtils;\nimport com.direwolf20.buildinggadgets.common.util.compression.DataCompressor;\nimport com.direwolf20.buildinggadgets.common.util.compression.DataDecompressor;\nimport com.direwolf20.buildinggadgets.common.util.ref.NBTKeys;\nimport com.direwolf20.buildinggadgets.common.util.tools.MathUtils;\nimport com.direwolf20.buildinggadgets.common.util.tools.RegistryUtils;\nimport com.google.common.collect.ImmutableMap;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction.Axis;\nimport net.minecraft.nbt.*;\nimport net.minecraft.world.level.block.Mirror;\nimport net.minecraft.world.level.block.Rotation;\n\nimport javax.annotation.Nullable;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.function.Function;\n\npublic final class Template {\n    public static Template deserialize(CompoundTag nbt, @Nullable TemplateHeader externalHeader, boolean persisted) {\n        ListTag posList = nbt.getList(NBTKeys.KEY_POS, Tag.TAG_LONG);\n        TemplateHeader.Builder header = TemplateHeader.builderFromNBT(nbt.getCompound(NBTKeys.KEY_HEADER));\n        if (externalHeader != null)\n            header = header.name(externalHeader.getName()).author(externalHeader.getAuthor());\n\n        DataDecompressor<ITileDataSerializer> serializerDecompressor = persisted ? new DataDecompressor<>(\n                nbt.getList(NBTKeys.KEY_SERIALIZER, Tag.TAG_STRING),\n                inbt -> RegistryUtils.getFromString(Registries.TILE_DATA_SERIALIZER_REGISTRY.get(), inbt.getAsString()),\n                value -> SerialisationSupport.dummyDataSerializer())\n                : null;\n\n        DataDecompressor<BlockData> dataDecompressor = new DataDecompressor<>(\n                nbt.getList(NBTKeys.KEY_DATA, Tag.TAG_COMPOUND),\n                inbt -> persisted ?\n                        BlockData.tryDeserialize((CompoundTag) inbt, serializerDecompressor, true) :\n                        BlockData.tryDeserialize((CompoundTag) inbt, false),\n                value -> BlockData.AIR);\n\n        ImmutableMap.Builder<BlockPos, BlockData> mapBuilder = ImmutableMap.builder();\n        for (Tag inbt : posList) {\n            LongTag longNBT = (LongTag) inbt;\n            BlockPos pos = MathUtils.posFromLong(longNBT.getAsLong());\n            BlockData data = dataDecompressor.apply(MathUtils.readStateId(longNBT.getAsLong()));\n            mapBuilder.put(pos, data);\n        }\n        return new Template(mapBuilder.build(), header.build());\n    }\n\n    private final ImmutableMap<BlockPos, BlockData> map;\n    private TemplateHeader header; //the only modification, this may ever receive, is evaluating the requiredItems!\n    private boolean isNormalized;\n\n    public Template(ImmutableMap<BlockPos, BlockData> map, TemplateHeader header) {\n        this(map, header, false);\n    }\n\n    private Template(ImmutableMap<BlockPos, BlockData> map, TemplateHeader header, boolean isNormalized) {\n        this.map = map;\n        this.header = header;\n        this.isNormalized = isNormalized;\n    }\n\n    public Template() {\n        this(ImmutableMap.of(), TemplateHeader.builder(Region.singleZero()).build());\n    }\n\n    public TemplateHeader getHeaderAndForceMaterials(BuildContext context) {\n        if (header.getRequiredItems() == null) {\n            MaterialList materialList = CommonUtils.estimateRequiredItems(\n                    createViewInContext(context),\n                    context,\n                    context.getPlayer() != null ?\n                            context.getPlayer().position().add(0, context.getPlayer().getEyeHeight(), 0) :\n                            null);\n            header = TemplateHeader.builderOf(header).requiredItems(materialList).build();\n        }\n        return getHeader();\n    }\n\n    public TemplateHeader getHeader() {\n        return header;\n    }\n\n    public IBuildView createViewInContext(BuildContext context) {\n        return PositionalBuildView.createUnsafe(context, map, header.getBoundingBox());\n    }\n\n    public CompoundTag serialize(boolean persisted) {\n        if (!isNormalized)\n            return normalize().serialize(persisted);\n        CompoundTag res = new CompoundTag();\n        ListTag posList = new ListTag();\n        DataCompressor<BlockData> blockDataCompressor = new DataCompressor<>();\n        DataCompressor<ITileDataSerializer> dataSerializerCompressor = new DataCompressor<>();\n        for (Map.Entry<BlockPos, BlockData> entry : map.entrySet()) {\n            long posEntry = MathUtils.includeStateId(MathUtils.posToLong(entry.getKey()), blockDataCompressor.applyAsInt(entry.getValue()));\n            posList.add(LongTag.valueOf(posEntry));\n        }\n        ListTag dataList = blockDataCompressor.write(d -> persisted ?\n                d.serialize(dataSerializerCompressor, true)\n                : d.serialize(false));\n        ListTag serializerList = persisted ? dataSerializerCompressor.write(s -> StringTag.valueOf(Registries.TILE_DATA_SERIALIZER_REGISTRY.get().getKey(s).toString())) : null;\n        res.put(NBTKeys.KEY_DATA, dataList);\n        res.put(NBTKeys.KEY_POS, posList);\n        res.put(NBTKeys.KEY_HEADER, header.toNBT(persisted));\n        if (persisted)\n            res.put(NBTKeys.KEY_SERIALIZER, serializerList);\n        return res;\n    }\n\n    public Template rotate(Rotation rotation) {\n        return rotate(Axis.Y, rotation);\n    }\n\n    public Template rotate(Axis axis, Rotation rotation) {\n        if (map.isEmpty()) //saves some time and prevents problems with enclosing builder\n            return this;\n        int[][] matrix = MathUtils.rotationMatrixFor(axis, rotation);\n        rotation = axis == Axis.Y ? rotation : Rotation.NONE; //BlockState's can only rotate around the y-Axis\n        ImmutableMap.Builder<BlockPos, BlockData> mapBuilder = ImmutableMap.builder();\n        Region.Builder regionBuilder = Region.enclosingBuilder();\n        for (Map.Entry<BlockPos, BlockData> entry : map.entrySet()) {\n            BlockPos newPos = MathUtils.matrixMul(matrix, entry.getKey());\n            mapBuilder.put(newPos, entry.getValue().rotate(rotation));\n            regionBuilder.enclose(newPos);\n        }\n        return new Template(mapBuilder.build(), TemplateHeader.builderOf(header, regionBuilder.build()).build()).normalize();\n    }\n\n    public Template mirror(Axis axis) {\n        int xFac = 1;\n        int zFac = 1;\n        Mirror mirror;\n        switch (axis) {\n            case X:\n                mirror = Mirror.LEFT_RIGHT;\n                zFac = -1;\n                break;\n            case Z:\n                mirror = Mirror.FRONT_BACK;\n                xFac = -1;\n                break;\n            default:\n                mirror = Mirror.NONE;\n        }\n        Region.Builder regionBuilder = Region.enclosingBuilder();\n        ImmutableMap.Builder<BlockPos, BlockData> mapBuilder = ImmutableMap.builder();\n        for (Map.Entry<BlockPos, BlockData> entry : map.entrySet()) {\n            BlockPos newPos = new BlockPos(entry.getKey().getX() * xFac, entry.getKey().getY(), entry.getKey().getZ() * zFac);\n            mapBuilder.put(newPos, entry.getValue().mirror(mirror));\n            regionBuilder.enclose(newPos);\n        }\n        return new Template(mapBuilder.build(), TemplateHeader.builderOf(header, regionBuilder.build()).build()).normalize();\n    }\n\n    public Template replace(Function<BlockPos, Optional<BlockData>> replacements) {\n        ImmutableMap.Builder<BlockPos, BlockData> mapBuilder = ImmutableMap.builder();\n        for (Map.Entry<BlockPos, BlockData> entry : map.entrySet()) {\n            mapBuilder.put(entry.getKey(), replacements.apply(entry.getKey()).orElse(entry.getValue()));\n        }\n        return new Template(mapBuilder.build(), header, isNormalized);\n    }\n\n    public Template withName(@Nullable String name) {\n        return new Template(map, TemplateHeader.builderOf(header).name(name).build());\n    }\n\n    public Template withNameAndAuthor(@Nullable String name, @Nullable String author) {\n        return new Template(map, TemplateHeader.builderOf(header).name(name).author(author).build());\n    }\n\n    public Template clearMaterials() {\n        return new Template(map, TemplateHeader.builderOf(header).requiredItems(null).build());\n    }\n\n    public Template normalize() {\n        if (isNormalized)\n            return this;\n        Region region = header.getBoundingBox();\n        ImmutableMap.Builder<BlockPos, BlockData> builder = ImmutableMap.builder();\n        for (Map.Entry<BlockPos, BlockData> entry : map.entrySet()) {\n            builder.put(entry.getKey().subtract(region.getMin()), entry.getValue());\n        }\n        return new Template(builder.build(), TemplateHeader.builderOf(header, region.inverseTranslate(region.getMin())).build(), true);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/template/TemplateHeader.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.template;\n\nimport com.direwolf20.buildinggadgets.common.tainted.building.Region;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.materials.MaterialList;\nimport com.direwolf20.buildinggadgets.common.util.exceptions.TemplateParseException.IllegalMinecraftVersionException;\nimport com.direwolf20.buildinggadgets.common.util.exceptions.TemplateParseException.UnknownTemplateVersionException;\nimport com.direwolf20.buildinggadgets.common.util.ref.JsonKeys;\nimport com.direwolf20.buildinggadgets.common.util.ref.NBTKeys;\nimport com.direwolf20.buildinggadgets.common.util.tools.JsonBiDiSerializer;\nimport com.google.common.base.Preconditions;\nimport com.google.common.collect.Multiset;\nimport com.google.gson.*;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.nbt.Tag;\nimport net.minecraft.resources.ResourceLocation;\nimport org.apache.maven.artifact.versioning.ComparableVersion;\n\nimport javax.annotation.Nonnull;\nimport javax.annotation.Nullable;\nimport java.lang.reflect.Type;\nimport java.util.Objects;\n\n/**\n * In-Memory representation of the '.th' (TemplateHeader) written in json Format to disk, in order to allow users to have some more general Information about the\n * {@link Template}. Only the boundingBox information is required. However it is advised to provide Users with as\n * much information as possible about the {@link Template} they would like to use.\n */\npublic final class TemplateHeader {\n    public static final String VERSION = \"2.1.0\";\n\n    /**\n     * Must be updated for each new MC version :cry:\n     */\n    public static final String HIGHEST_MC_VERSION = \"1.17.1\";\n\n    /**\n     * The lowest possible minecraft version we support. This could be lower but seeing as the mod only uses\n     * this new schema from 1.14+, this seems like a good starting point. If the game changes anything dramatically\n     * then this will need to be bumped up to reject older, likely broken versions\n     */\n    public static final String LOWEST_MC_VERSION = \"1.14.4\";\n    \n    private static final ComparableVersion COMP_VERSION = new ComparableVersion(VERSION);\n    private static final ComparableVersion HIGHEST_MC_COMP = new ComparableVersion(HIGHEST_MC_VERSION);\n    private static final ComparableVersion LOWEST_MC_COMP = new ComparableVersion(LOWEST_MC_VERSION);\n\n    // todo: add support for previous templates from older versions\n    private static final JsonBiDiSerializer<TemplateHeader> BI_DI_SERIALIZER = new JsonBiDiSerializer<TemplateHeader>() {\n        @Override\n        public TemplateHeader deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {\n            JsonObject data = json.getAsJsonObject();\n\n            String mcVersion = data.getAsJsonPrimitive(JsonKeys.HEADER_MC_VERSION).getAsString();\n            ComparableVersion templateMcVersion = new ComparableVersion(mcVersion);\n\n            if (templateMcVersion.compareTo(LOWEST_MC_COMP) < 0 || templateMcVersion.compareTo(HIGHEST_MC_COMP) > 0)\n                throw new IllegalMinecraftVersionException(mcVersion);\n\n            String version = data.getAsJsonPrimitive(JsonKeys.HEADER_VERSION).getAsString();\n            if (new ComparableVersion(version).compareTo(COMP_VERSION) > 0)\n                throw new UnknownTemplateVersionException(version);\n\n            TemplateHeader.Builder builder;\n            if (data.has(JsonKeys.HEADER_BOUNDING_BOX))\n                builder = TemplateHeader.builder(context.deserialize(data.get(JsonKeys.HEADER_BOUNDING_BOX), Region.class));\n            else\n                builder = TemplateHeader.builder(Region.singleZero());\n\n            if (data.has(JsonKeys.HEADER_NAME))\n                builder.name(context.deserialize(data.get(JsonKeys.HEADER_NAME), String.class));\n\n            if (data.has(JsonKeys.HEADER_AUTHOR))\n                builder.author(context.deserialize(data.get(JsonKeys.HEADER_AUTHOR), String.class));\n\n            if (data.has(JsonKeys.HEADER_REQUIRED_ITEMS))\n                builder.requiredItems(context.deserialize(data.get(JsonKeys.HEADER_REQUIRED_ITEMS), MaterialList.class));\n\n            return builder.build();\n        }\n\n        @Override\n        public JsonElement serialize(TemplateHeader src, Type typeOfSrc, JsonSerializationContext context) {\n            JsonObject object = new JsonObject();\n            object.addProperty(JsonKeys.HEADER_VERSION, VERSION);\n            object.addProperty(JsonKeys.HEADER_MC_VERSION, HIGHEST_MC_VERSION);\n            if (src.getName() != null)\n                object.addProperty(JsonKeys.HEADER_NAME, src.getName());\n            if (src.getAuthor() != null)\n                object.addProperty(JsonKeys.HEADER_AUTHOR, src.getAuthor());\n            object.add(JsonKeys.HEADER_BOUNDING_BOX, context.serialize(src.getBoundingBox(), Region.class));\n            if (src.getRequiredItems() != null)\n                object.add(JsonKeys.HEADER_REQUIRED_ITEMS, context.serialize(src.getRequiredItems(), MaterialList.class));\n            return object;\n        }\n    };\n    /**\n     * Creates a new {@link Builder} which can be used to create {@code TemplateHeader} objects.\n     *\n     * @param boundingBox a {@link net.minecraft.core.BlockPos} representing the x-y-z size of the corresponding {@link Template}.\n     * @return A new {@link Builder} for the specified serializer and boundingBox.\n     */\n    public static Builder builder(Region boundingBox) {\n        return new Builder(boundingBox);\n    }\n\n    /**\n     * @param header The {@code TemplateHeader} to copy\n     * @return a {@link Builder} with all values predefined to the values passed into the header\n     */\n    public static Builder builderOf(TemplateHeader header) {\n        return builderOf(header, header.getBoundingBox());\n    }\n\n    /**\n     * @param boundingBox the {@link Region} to use\n     * @param header      The {@code TemplateHeader} to copy\n     * @return a {@link Builder} with all values predefined to the values passed into the header, except for serializer and boundBox\n     */\n    public static Builder builderOf(TemplateHeader header, Region boundingBox) {\n        return builder(boundingBox)\n                .author(header.getAuthor())\n                .name(header.getName())\n                .requiredItems(header.getRequiredItems());\n    }\n\n    public static Builder builderFromNBT(CompoundTag nbt, boolean persisted) {\n        Preconditions.checkArgument(nbt.contains(NBTKeys.KEY_BOUNDS, Tag.TAG_COMPOUND),\n                \"Cannot construct a TemplateHeader without '\" + NBTKeys.KEY_BOUNDS + \"'!\");\n        Region region = Region.deserializeFrom(nbt.getCompound(NBTKeys.KEY_BOUNDS));\n        Builder builder = builder(region);\n        if (nbt.contains(NBTKeys.KEY_NAME, Tag.TAG_STRING))\n            builder.name(nbt.getString(NBTKeys.KEY_NAME));\n        if (nbt.contains(NBTKeys.KEY_AUTHOR, Tag.TAG_STRING))\n            builder.author(nbt.getString(NBTKeys.KEY_AUTHOR));\n        if (nbt.contains(NBTKeys.KEY_MATERIALS, Tag.TAG_COMPOUND))\n            builder.requiredItems(MaterialList.deserialize(nbt.getCompound(NBTKeys.KEY_MATERIALS), persisted));\n        return builder;\n    }\n\n    public static Builder builderFromNBT(CompoundTag nbt) {\n        return builderFromNBT(nbt, true);\n    }\n\n    public static TemplateHeader fromNBT(CompoundTag nbt) {\n        return TemplateHeader.builderFromNBT(nbt).build();\n    }\n\n    public static GsonBuilder appendHeaderSpecification(GsonBuilder builder, boolean printName, boolean extended) {\n        return builder\n                .setPrettyPrinting()\n                .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)\n                .registerTypeAdapter(ResourceLocation.class, new JsonBiDiSerializer<ResourceLocation>() {\n                    @Override\n                    public ResourceLocation deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {\n                        return new ResourceLocation(json.getAsJsonPrimitive().getAsString());\n                    }\n\n                    @Override\n                    public JsonElement serialize(ResourceLocation src, Type typeOfSrc, JsonSerializationContext context) {\n                        return new JsonPrimitive(src.toString());\n                    }\n                })\n                .registerTypeAdapter(MaterialList.class, new MaterialList.JsonSerializer(printName, extended))\n                .registerTypeAdapter(TemplateHeader.class, BI_DI_SERIALIZER);\n    }\n\n    @Nullable\n    private final String name;\n    @Nullable\n    private final String author;\n    @Nonnull\n    private final Region boundingBox;\n    @Nullable\n    private final MaterialList requiredItems;\n\n    private TemplateHeader(@Nullable String name, @Nullable String author, @Nullable MaterialList requiredItems, @Nonnull Region boundingBox) {\n        this.name = name;\n        this.author = author;\n        this.requiredItems = requiredItems;\n        this.boundingBox = Objects.requireNonNull(boundingBox);\n    }\n\n    /**\n     * @return The optional name of the corresponding {@link Template}. Null if not present.\n     */\n    @Nullable\n    public String getName() {\n        return name;\n    }\n\n    /**\n     * @return The optional author of the corresponding {@link Template}. Null if not present.\n     */\n    @Nullable\n    public String getAuthor() {\n        return author;\n    }\n\n    /**\n     * @return The optional set of required Items of the corresponding {@link Template}. Null if not present.\n     */\n    @Nullable\n    public MaterialList getRequiredItems() {\n        return requiredItems;\n    }\n\n    /**\n     * @return The boundingBox of the corresponding {@link Template}.\n     */\n    public Region getBoundingBox() {\n        return boundingBox;\n    }\n\n    /**\n     * @param persisted whether or not the save may be persisted\n     * @return A new {@link CompoundTag} which can be used for {@link #fromNBT(CompoundTag)}\n     * @implNote If this is called with persisted=false then this will never write {@link #getRequiredItems()}.\n     * This is done in order not to prevent updates from changing the required Items for an {@link Template}.\n     */\n    public CompoundTag toNBT(boolean persisted) {\n        CompoundTag nbt = new CompoundTag();\n        nbt.put(NBTKeys.KEY_BOUNDS, getBoundingBox().serialize());\n        if (getName() != null)\n            nbt.putString(NBTKeys.KEY_NAME, getName());\n        if (getAuthor() != null)\n            nbt.putString(NBTKeys.KEY_AUTHOR, getAuthor());\n        if (! persisted && getRequiredItems() != null)\n            nbt.put(NBTKeys.KEY_MATERIALS, getRequiredItems().serialize(persisted));\n        return nbt;\n    }\n\n\n    public String toJson(boolean printName, boolean extended) {\n        return appendHeaderSpecification(new GsonBuilder(), printName, extended)\n                .create()\n                .toJson(this);\n    }\n\n    /**\n     * {@code Builder} for {@link TemplateHeader}. An instance of this class can be acquired via {@link #builder(Region)}.\n     */\n    public static final class Builder {\n        @Nullable\n        private String name;\n        @Nullable\n        private String author;\n        @Nullable\n        private MaterialList requiredItems;\n        @Nonnull\n        private Region boundingBox;\n\n        private Builder(Region boundingBox) {\n            this.boundingBox = Objects.requireNonNull(boundingBox);\n        }\n\n        /**\n         * @param boundingBox The new boundingBox to be used. May not be null!\n         * @return The {@code Builder} instance to allow for method chaining\n         */\n        public Builder bounds(Region boundingBox) {\n            this.boundingBox = Objects.requireNonNull(boundingBox);\n            return this;\n        }\n\n        /**\n         * Set's the name for the resulting {@link TemplateHeader}\n         *\n         * @param name The name of the corresponding {@link Template}.\n         * @return The {@code Builder} instance to allow for method chaining\n         */\n        public Builder name(@Nullable String name) {\n            this.name = name;\n            return this;\n        }\n\n        /**\n         * Set's the author for the resulting {@link TemplateHeader}\n         *\n         * @param author The author of the corresponding {@link Template}.\n         * @return The {@code Builder} instance to allow for method chaining\n         */\n        public Builder author(@Nullable String author) {\n            this.author = author;\n            return this;\n        }\n\n        /**\n         * Set's the requiredItems for the resulting {@link TemplateHeader}\n         *\n         * @param requiredItems The requiredItems of the corresponding {@link Template}.\n         *                      Null values will be converted to an empty {@link Multiset}.\n         * @return The {@code Builder} instance to allow for method chaining\n         */\n        public Builder requiredItems(@Nullable MaterialList requiredItems) {\n            this.requiredItems = requiredItems;\n            return this;\n        }\n\n        /**\n         * @return A new {@link TemplateHeader} with the specified properties.\n         */\n        public TemplateHeader build() {\n            return new TemplateHeader(name, author, requiredItems, boundingBox);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/template/TemplateIO.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.template;\n\nimport com.direwolf20.buildinggadgets.common.tainted.building.view.BuildContext;\nimport com.direwolf20.buildinggadgets.common.util.exceptions.TemplateReadException;\nimport com.direwolf20.buildinggadgets.common.util.exceptions.TemplateReadException.CorruptDataException;\nimport com.direwolf20.buildinggadgets.common.util.exceptions.TemplateReadException.CorruptJsonException;\nimport com.direwolf20.buildinggadgets.common.util.exceptions.TemplateReadException.DataCannotBeReadException;\nimport com.direwolf20.buildinggadgets.common.util.exceptions.TemplateReadException.IllegalNBTDataException;\nimport com.direwolf20.buildinggadgets.common.util.exceptions.TemplateWriteException;\nimport com.direwolf20.buildinggadgets.common.util.exceptions.TemplateWriteException.DataCannotBeWrittenException;\nimport com.google.gson.Gson;\nimport com.google.gson.GsonBuilder;\nimport com.google.gson.JsonSyntaxException;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.nbt.NbtIo;\n\nimport javax.annotation.Nullable;\nimport java.io.*;\nimport java.util.Base64;\n\npublic final class TemplateIO {\n    private static final Gson GSON = TemplateHeader.appendHeaderSpecification(new GsonBuilder(), false, true).create();\n    private TemplateIO() {}\n\n    public static void writeTemplate(Template template, OutputStream stream) throws TemplateWriteException {\n        writeTemplate(template, stream, true);\n    }\n\n    public static void writeTemplate(Template template, OutputStream stream, boolean persisted) throws TemplateWriteException {\n        CompoundTag nbt = template.serialize(persisted);\n        try {\n            NbtIo.writeCompressed(nbt, stream);\n        } catch (IOException e) {\n            throw new DataCannotBeWrittenException(e, nbt);\n        }\n    }\n\n    /**\n     * @see #readTemplate(InputStream, TemplateHeader, boolean)\n     */\n    public static Template readTemplate(InputStream stream, @Nullable TemplateHeader header) throws TemplateReadException {\n        return readTemplate(stream, header, true);\n    }\n\n    /**\n     * @param stream    the Stream to read from\n     * @param persisted whether this was written as persisted.\n     * @param header    The TemplateHeader if present. Null otherwise.\n     * @return A TemplateItem if the serializer is known. Null if not.\n     * @throws IOException if a read error occurs or the read nbt does not match the format written by {@link #writeTemplate(Template, OutputStream, boolean)}\n     */\n    public static Template readTemplate(InputStream stream, @Nullable TemplateHeader header, boolean persisted) throws TemplateReadException {\n        try {\n            return readTemplate(NbtIo.readCompressed(stream), header, persisted);\n        } catch (IOException e) {\n            throw new DataCannotBeReadException(e);\n        }\n    }\n\n    /**\n     * @param nbt       the Compound to read from\n     * @param persisted whether this was written as persisted.\n     * @param header    The TemplateHeader if present. Null otherwise.\n     * @return A TemplateItem if the serializer is known. Null if not.\n     */\n    public static Template readTemplate(CompoundTag nbt, @Nullable TemplateHeader header, boolean persisted) throws TemplateReadException {\n        try {\n            return Template.deserialize(nbt, header, persisted);\n        } catch (Exception e) {\n            throw new IllegalNBTDataException(e, nbt);\n        }\n    }\n\n    public static void writeTemplateJson(Template template, OutputStream stream) throws TemplateWriteException {\n        writeTemplateJson(template, stream, null);\n    }\n\n    public static void writeTemplateJson(Template template, OutputStream stream, @Nullable BuildContext context) throws TemplateWriteException {\n        GSON.toJson(TemplateJsonRepresentation.ofTemplate(template, context), new OutputStreamWriter(stream));\n    }\n\n    public static String writeTemplateJson(Template template) throws TemplateWriteException {\n        return writeTemplateJson(template, (BuildContext) null);\n    }\n\n    public static String writeTemplateJson(Template template, @Nullable BuildContext context) throws TemplateWriteException {\n        return GSON.toJson(TemplateJsonRepresentation.ofTemplate(template, context));\n    }\n\n    public static Template readTemplateFromJson(String json) throws TemplateReadException {\n        try {\n            return GSON.fromJson(json, TemplateJsonRepresentation.class).getTemplate();\n        } catch (JsonSyntaxException e) {\n            throw new CorruptJsonException(e);\n        }\n    }\n\n    public static Template readTemplateFromJson(InputStream stream) throws TemplateReadException {\n        try {\n            return GSON.fromJson(new InputStreamReader(stream), TemplateJsonRepresentation.class).getTemplate();\n        } catch (JsonSyntaxException e) {\n            throw new CorruptJsonException(e);\n        }\n    }\n\n    private static final class TemplateJsonRepresentation {\n        public static TemplateJsonRepresentation ofTemplate(Template template, @Nullable BuildContext context) throws TemplateWriteException {\n            TemplateHeader header = context != null ?\n                    template.getHeaderAndForceMaterials(context) :\n                    template.getHeader();\n            ByteArrayOutputStream baos = new ByteArrayOutputStream();\n            writeTemplate(template, baos);\n            String base64 = Base64.getEncoder().encodeToString(baos.toByteArray());\n            return new TemplateJsonRepresentation(header, base64);\n        }\n\n        private final TemplateHeader header;\n        private final String body;\n\n        private TemplateJsonRepresentation(TemplateHeader header, String body) {\n            this.header = header;\n            this.body = body;\n        }\n\n        private TemplateHeader getHeader() {\n            return header;\n        }\n\n        private String getBody() {\n            return body;\n        }\n\n        private Template getTemplate() throws TemplateReadException {\n            try {\n                byte[] bytes = Base64.getDecoder().decode(body);\n                CompoundTag nbt;\n                nbt = NbtIo.readCompressed(new ByteArrayInputStream(bytes));\n                return Template.deserialize(nbt, header, true);\n            } catch (IOException | NullPointerException e) {\n                throw new CorruptDataException(e, body);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/template/TemplateKey.java",
    "content": "package com.direwolf20.buildinggadgets.common.tainted.template;\n\nimport javax.annotation.Nullable;\nimport java.util.UUID;\nimport java.util.function.Supplier;\n\n/**\n * A very simple {@link ITemplateKey} which allows to query an {@link ITemplateProvider} for a specific Template, without\n * having the CapabilityProvider at hand. (For example useful for packets)\n */\npublic final class TemplateKey implements ITemplateKey {\n    @Nullable\n    private UUID id;\n\n    public TemplateKey() {\n        this(null);\n    }\n\n    public TemplateKey(@Nullable UUID id) {\n        this.id = id;\n    }\n\n    @Override\n    public UUID getTemplateId(Supplier<UUID> freeIdAllocator) {\n        if (id == null)\n            setUUID(freeIdAllocator.get());\n        return id;\n    }\n\n    @Nullable\n    public UUID getId() {\n        return id;\n    }\n\n    public TemplateKey setUUID(@Nullable UUID id) {\n        this.id = id;\n        return this;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tainted/template/package-info.java",
    "content": "/**\n * Contains the core template api with the {@link com.direwolf20.buildinggadgets.api.template.Template} class.\n * <p>\n * Templates are 3D-structures which can be build in a certain context (see {@link com.direwolf20.buildinggadgets.common.tainted.building.view building}),\n * transformed in various ways (see {@link com.direwolf20.buildinggadgets.api.template.transaction transactions}) and serialized (see\n * {@link com.direwolf20.buildinggadgets.api.serialisation serialisation}). Currently there are 2 major implementations\n * {@link com.direwolf20.buildinggadgets.api.template.ImmutableTemplate} and {@link com.direwolf20.buildinggadgets.api.template.DelegatingTemplate}.\n */\n@ParametersAreNonnullByDefault\n@MethodsReturnNonnullByDefault\npackage com.direwolf20.buildinggadgets.common.tainted.template;\n\nimport net.minecraft.MethodsReturnNonnullByDefault;\n\nimport javax.annotation.ParametersAreNonnullByDefault;"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tileentities/ConstructionBlockTileEntity.java",
    "content": "package com.direwolf20.buildinggadgets.common.tileentities;\n\nimport com.direwolf20.buildinggadgets.common.tainted.building.BlockData;\nimport com.direwolf20.buildinggadgets.common.util.ref.NBTKeys;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.network.Connection;\nimport net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;\nimport net.minecraft.world.level.block.entity.BlockEntity;\nimport net.minecraftforge.client.model.data.ModelData;\nimport net.minecraftforge.client.model.data.ModelDataManager;\nimport net.minecraftforge.client.model.data.ModelProperty;\nimport org.jetbrains.annotations.NotNull;\n\nimport javax.annotation.Nonnull;\n\npublic class ConstructionBlockTileEntity extends BlockEntity {\n\n    private BlockData blockState;\n    public static final ModelProperty<BlockState> FACADE_STATE = new ModelProperty<>();\n\n    public ConstructionBlockTileEntity(BlockPos pos, BlockState state) {\n        super(OurTileEntities.CONSTRUCTION_BLOCK_TILE_ENTITY.get(), pos, state);\n    }\n\n    public void setBlockState(BlockData state) {\n        blockState = state;\n        markDirtyClient();\n    }\n\n    // TODO: query simulated Tile, if exists, and relay model data...\n    @Override\n    public @NotNull ModelData getModelData() {\n        if (blockState == null) {\n            return super.getModelData();\n        }\n\n        BlockState state = blockState.getState();\n        return ModelData.builder().with(FACADE_STATE, state).build();\n    }\n\n    @Nonnull\n    @Override\n    public BlockState getBlockState() {\n        return getConstructionBlockData().getState();\n    }\n\n    @Nonnull\n    public BlockData getConstructionBlockData() {\n        if (blockState == null)\n            return BlockData.AIR;\n        return blockState;\n    }\n\n    @Override\n    public void load(CompoundTag nbt) {\n        super.load(nbt);\n        blockState = BlockData.tryDeserialize(nbt.getCompound(NBTKeys.TE_CONSTRUCTION_STATE), true);\n        markDirtyClient();\n    }\n\n    @Nonnull\n    @Override\n    protected void saveAdditional(CompoundTag compound) {\n        if (blockState != null) {\n            compound.put(NBTKeys.TE_CONSTRUCTION_STATE, blockState.serialize(true));\n        }\n        super.saveAdditional(compound);\n    }\n\n    private void markDirtyClient() {\n        setChanged();\n        if (getLevel() != null) {\n            BlockState state = getLevel().getBlockState(getBlockPos());\n            getLevel().sendBlockUpdated(getBlockPos(), state, state, 3);\n        }\n    }\n\n    @Nonnull\n    @Override\n    public CompoundTag getUpdateTag() {\n        CompoundTag updateTag = super.getUpdateTag();\n        saveAdditional(updateTag);\n        return updateTag;\n    }\n\n    @Override\n    public ClientboundBlockEntityDataPacket getUpdatePacket() {\n        CompoundTag nbtTag = new CompoundTag();\n        saveAdditional(nbtTag);\n        return ClientboundBlockEntityDataPacket.create(this);\n    }\n\n    @Override\n    public void onDataPacket(Connection net, ClientboundBlockEntityDataPacket packet) {\n        BlockData oldMimicBlock = getConstructionBlockData();\n        CompoundTag tagCompound = packet.getTag();\n        super.onDataPacket(net, packet);\n        deserializeNBT(tagCompound);\n\n        if (level != null && level.isClientSide) {\n            // If needed send a render update.\n            if (! getConstructionBlockData().equals(oldMimicBlock)) {\n                level.blockEntityChanged(getBlockPos());\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tileentities/EffectBlockTileEntity.java",
    "content": "package com.direwolf20.buildinggadgets.common.tileentities;\n\nimport com.direwolf20.buildinggadgets.common.blocks.EffectBlock.Mode;\nimport com.direwolf20.buildinggadgets.common.tainted.Tainted;\nimport com.direwolf20.buildinggadgets.common.tainted.building.BlockData;\nimport com.direwolf20.buildinggadgets.common.tainted.building.tilesupport.TileSupport;\nimport com.direwolf20.buildinggadgets.common.util.ref.NBTKeys;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.nbt.Tag;\nimport net.minecraft.network.Connection;\nimport net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.entity.BlockEntity;\nimport net.minecraft.world.level.block.state.BlockState;\n\nimport javax.annotation.Nonnull;\nimport javax.annotation.Nullable;\n\n@Tainted(reason = \"Used blockData and a stupid non-centralised callback system\")\npublic class EffectBlockTileEntity extends BlockEntity {\n    /**\n     * Even though this is called \"rendered\", is will be used for replacement under normal conditions.\n     */\n    private BlockData renderedBlock;\n    /**\n     * A copy of the target block, used for inheriting data for {@link Mode#REPLACE}\n     */\n    private BlockData sourceBlock;\n\n    private Mode mode = null;\n    private boolean usePaste;\n\n    private int ticks;\n\n    public EffectBlockTileEntity(BlockPos pos, BlockState state) {\n        super(OurTileEntities.EFFECT_BLOCK_TILE_ENTITY.get(), pos, state);\n    }\n\n    public void initializeData(BlockState curState, @Nullable BlockEntity te, BlockData replacementBlock, Mode mode, boolean usePaste) {\n        // Minecraft will reuse a tile entity object at a location where the block got removed, but the modification is still buffered, and the block got restored again\n        // If we don't reset this here, the 2nd phase of REPLACE will simply finish immediately because the tile entity object is reused\n        this.ticks = 0;\n        // Again we don't check if the data has been set or not because there is a chance that this tile object gets reused\n        this.sourceBlock = replacementBlock;\n\n        this.mode = mode;\n        this.usePaste = usePaste;\n\n        if (mode == Mode.REPLACE)\n            this.renderedBlock = te instanceof ConstructionBlockTileEntity ? ((ConstructionBlockTileEntity) te).getConstructionBlockData() : TileSupport.createBlockData(curState, te);\n        else\n            this.renderedBlock = te instanceof ConstructionBlockTileEntity ? ((ConstructionBlockTileEntity) te).getConstructionBlockData() : replacementBlock;\n    }\n\n    public static void tick(Level level, BlockPos blockPos, BlockState state, EffectBlockTileEntity blockEntity) {\n        blockEntity.ticks++;\n        if (blockEntity.ticks >= blockEntity.getLifespan()) {\n            blockEntity.complete();\n        }\n    }\n\n    private void complete() {\n        if (level == null || level.isClientSide || mode == null || renderedBlock == null)\n            return;\n\n        mode.onBuilderRemoved(this);\n    }\n\n    public BlockData getRenderedBlock() {\n        return renderedBlock;\n    }\n\n    public BlockData getSourceBlock() {\n        return sourceBlock;\n    }\n\n    public Mode getReplacementMode() {\n        return mode;\n    }\n\n    public boolean isUsingPaste() {\n        return usePaste;\n    }\n\n    public int getTicksExisted() {\n        return ticks;\n    }\n\n    public int getLifespan() {\n        return 20;\n    }\n\n    @Override\n    public ClientboundBlockEntityDataPacket getUpdatePacket() {\n        // Vanilla uses the type parameter to indicate which type of tile entity (command block, skull, or beacon?) is receiving the packet, but it seems like Forge has overridden this behavior\n        return ClientboundBlockEntityDataPacket.create(this);\n    }\n\n    @Nonnull\n    @Override\n    public CompoundTag getUpdateTag() {\n        CompoundTag compoundTag = new CompoundTag();\n        saveAdditional(compoundTag);\n        return compoundTag;\n    }\n\n    @Override\n    public void handleUpdateTag(CompoundTag tag) {\n        deserializeNBT(tag);\n    }\n\n    @Override\n    public void onDataPacket(Connection net, ClientboundBlockEntityDataPacket pkt) {\n        deserializeNBT(pkt.getTag());\n    }\n\n    @Nonnull\n    @Override\n    protected void saveAdditional(CompoundTag compound) {\n        if (mode != null && renderedBlock != null && sourceBlock != null) {\n            compound.putInt(NBTKeys.GADGET_TICKS, ticks);\n            compound.putInt(NBTKeys.GADGET_MODE, mode.ordinal());\n            compound.put(NBTKeys.GADGET_REPLACEMENT_BLOCK, renderedBlock.serialize(true));\n            compound.put(NBTKeys.GADGET_SOURCE_BLOCK, sourceBlock.serialize(true));\n            compound.putBoolean(NBTKeys.GADGET_USE_PASTE, usePaste);\n        }\n        super.saveAdditional(compound);\n    }\n\n    @Override\n    public void load(CompoundTag nbt) {\n        super.load(nbt);\n\n        if (nbt.contains(NBTKeys.GADGET_TICKS, Tag.TAG_INT) &&\n                nbt.contains(NBTKeys.GADGET_MODE, Tag.TAG_INT) &&\n                nbt.contains(NBTKeys.GADGET_SOURCE_BLOCK, Tag.TAG_COMPOUND) &&\n                nbt.contains(NBTKeys.GADGET_REPLACEMENT_BLOCK, Tag.TAG_COMPOUND) &&\n                nbt.contains(NBTKeys.GADGET_USE_PASTE)) {\n\n            ticks = nbt.getInt(NBTKeys.GADGET_TICKS);\n            mode = Mode.VALUES[nbt.getInt(NBTKeys.GADGET_MODE)];\n            renderedBlock = BlockData.tryDeserialize(nbt.getCompound(NBTKeys.GADGET_REPLACEMENT_BLOCK), true);\n            sourceBlock = BlockData.tryDeserialize(nbt.getCompound(NBTKeys.GADGET_SOURCE_BLOCK), true);\n            usePaste = nbt.getBoolean(NBTKeys.GADGET_USE_PASTE);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tileentities/OurTileEntities.java",
    "content": "package com.direwolf20.buildinggadgets.common.tileentities;\n\nimport com.direwolf20.buildinggadgets.common.blocks.OurBlocks;\nimport com.direwolf20.buildinggadgets.common.util.ref.Reference;\nimport net.minecraft.world.level.block.entity.BlockEntityType;\nimport net.minecraftforge.registries.DeferredRegister;\nimport net.minecraftforge.registries.ForgeRegistries;\nimport net.minecraftforge.registries.RegistryObject;\n\npublic final class OurTileEntities {\n    public static final DeferredRegister<BlockEntityType<?>> TILE_ENTITIES = DeferredRegister.create(ForgeRegistries.BLOCK_ENTITY_TYPES, Reference.MODID);\n\n    public static final RegistryObject<BlockEntityType<EffectBlockTileEntity>> EFFECT_BLOCK_TILE_ENTITY =\n            TILE_ENTITIES.register(\"effect_block_tile\", () -> BlockEntityType.Builder.of(EffectBlockTileEntity::new, OurBlocks.EFFECT_BLOCK.get()).build(null));\n    public static final RegistryObject<BlockEntityType<ConstructionBlockTileEntity>> CONSTRUCTION_BLOCK_TILE_ENTITY =\n            TILE_ENTITIES.register(\"construction_tile\", () -> BlockEntityType.Builder.of(ConstructionBlockTileEntity::new, OurBlocks.CONSTRUCTION_BLOCK.get()).build(null));\n    public static final RegistryObject<BlockEntityType<TemplateManagerTileEntity>> TEMPLATE_MANAGER_TILE_ENTITY =\n            TILE_ENTITIES.register(\"template_manager_tile\", () -> BlockEntityType.Builder.of(TemplateManagerTileEntity::new, OurBlocks.TEMPLATE_MANGER_BLOCK.get()).build(null));\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/tileentities/TemplateManagerTileEntity.java",
    "content": "package com.direwolf20.buildinggadgets.common.tileentities;\n\nimport com.direwolf20.buildinggadgets.common.capability.CapabilityTemplate;\nimport com.direwolf20.buildinggadgets.common.containers.TemplateManagerContainer;\nimport com.direwolf20.buildinggadgets.common.util.ref.NBTKeys;\nimport com.direwolf20.buildinggadgets.common.util.ref.Reference.ItemReference;\nimport com.google.common.base.Preconditions;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.core.registries.Registries;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.tags.TagKey;\nimport net.minecraft.world.MenuProvider;\nimport net.minecraft.world.entity.player.Inventory;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.inventory.AbstractContainerMenu;\nimport net.minecraft.world.item.Item;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.block.entity.BlockEntity;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.phys.Vec3;\nimport net.minecraftforge.common.capabilities.Capability;\nimport net.minecraftforge.common.capabilities.ForgeCapabilities;\nimport net.minecraftforge.common.util.LazyOptional;\nimport net.minecraftforge.items.IItemHandler;\nimport net.minecraftforge.items.ItemStackHandler;\n\nimport javax.annotation.Nonnull;\nimport javax.annotation.Nullable;\n\npublic class TemplateManagerTileEntity extends BlockEntity implements MenuProvider {\n    public static final TagKey<Item> TEMPLATE_CONVERTIBLES = TagKey.create(Registries.ITEM, ItemReference.TAG_TEMPLATE_CONVERTIBLE);\n\n    public static final int SIZE = 2;\n\n    public TemplateManagerTileEntity(BlockPos pos, BlockState state) {\n        super(OurTileEntities.TEMPLATE_MANAGER_TILE_ENTITY.get(), pos, state);\n    }\n\n    // This item handler will hold our inventory slots\n    private final ItemStackHandler itemStackHandler = new ItemStackHandler(SIZE) {\n        @Override\n        protected void onContentsChanged(int slot) {\n            // We need to tell the tile entity that something has changed so\n            // that the chest contents is persisted\n            TemplateManagerTileEntity.this.setChanged();\n        }\n\n        private boolean isTemplateStack(ItemStack stack) {\n            return stack.getCapability(CapabilityTemplate.TEMPLATE_KEY_CAPABILITY).isPresent();\n        }\n\n        @Override\n        public int getSlotLimit(int slot) {\n            return 1;\n        }\n\n        @Override\n        public boolean isItemValid(int slot, @Nonnull ItemStack stack) {\n            return (slot == 0 && isTemplateStack(stack)) ||\n                    (slot == 1 && (isTemplateStack(stack) || stack.is(TEMPLATE_CONVERTIBLES)));\n        }\n    };\n    private LazyOptional<IItemHandler> handlerOpt;\n\n    @Override\n    @Nonnull\n    public Component getDisplayName() {\n        return Component.literal(\"Template Manager GUI\");\n    }\n\n    @Nullable\n    @Override\n    public AbstractContainerMenu createMenu(int windowId, @Nonnull Inventory playerInventory, @Nonnull Player playerEntity) {\n        Preconditions.checkArgument(getLevel() != null);\n        return new TemplateManagerContainer(windowId, playerInventory, this);\n    }\n\n    @Override\n    public void onLoad() {\n        onChunkUnloaded(); //clear it away if it is still present\n        handlerOpt = LazyOptional.of(() -> itemStackHandler);\n    }\n\n    @Override\n    public void load(CompoundTag nbt) {\n        super.load(nbt);\n\n        if (nbt.contains(NBTKeys.TE_TEMPLATE_MANAGER_ITEMS))\n            itemStackHandler.deserializeNBT(nbt.getCompound(NBTKeys.TE_TEMPLATE_MANAGER_ITEMS));\n    }\n\n    @Nonnull\n    @Override\n    protected void saveAdditional(CompoundTag compound) {\n        compound.put(NBTKeys.TE_TEMPLATE_MANAGER_ITEMS, itemStackHandler.serializeNBT());\n        super.saveAdditional(compound);\n    }\n\n    public boolean canInteractWith(Player playerIn) {\n        // If we are too far away (>4 blocks) from this tile entity you cannot use it\n        return !isRemoved() && playerIn.distanceToSqr(Vec3.atLowerCornerOf(worldPosition).add(0.5D, 0.5D, 0.5D)) <= 64D;\n    }\n\n    @Nonnull\n    @Override\n    public <T> LazyOptional<T> getCapability(@Nonnull Capability<T> cap, final @Nullable Direction side) {\n        if (cap == ForgeCapabilities.ITEM_HANDLER && handlerOpt != null)\n            return handlerOpt.cast();\n        return super.getCapability(cap, side);\n    }\n\n    @Override\n    public void onChunkUnloaded() {\n        if (handlerOpt != null) {\n            handlerOpt.invalidate();\n            handlerOpt = null;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/util/Additions.java",
    "content": "package com.direwolf20.buildinggadgets.common.util;\n\nimport com.direwolf20.buildinggadgets.common.tainted.building.Region;\nimport com.direwolf20.buildinggadgets.common.util.lang.ITranslationProvider;\nimport com.direwolf20.buildinggadgets.common.util.lang.Styles;\nimport com.direwolf20.buildinggadgets.common.util.ref.Reference;\nimport com.google.common.collect.ImmutableList;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.core.BlockPos;\n\nimport javax.annotation.Nonnegative;\nimport javax.annotation.Nonnull;\nimport java.util.List;\nimport java.util.Random;\n\n/**\n * Dear viewer, you have found our Easter Eggs, enjoy but please don't tell people who haven't seen this yet (directly, hinting that there might be something is allowed ;) ). :)\n */\npublic final class Additions {\n    private static final Random EASTER_RAND = new Random();\n\n    private enum NineByNineTranslation implements ITranslationProvider {\n        FOR_THE_COLLECTION(\".for_the_collection\"),\n        JUST_LIKE_DIRE(\".just_like_dire\"),\n        DIRE_WOULD_BE_PROUD(\".dire_would_be_proud\"),\n        NO_DIRE_WIRE_PLEASE(\".no_dire_wire\");\n        private static final List<ITranslationProvider> VALUES = ImmutableList.copyOf(values());\n        private static final String PREFIX = Reference.MODID + \".easter_eggs.9x9\";\n        private final String key;\n        private final int argCount;\n\n        NineByNineTranslation(@Nonnull String key, @Nonnegative int argCount) {\n            this.key = PREFIX + key;\n            this.argCount = argCount;\n        }\n\n        NineByNineTranslation(@Nonnull String key) {\n            this(key, 0);\n        }\n\n        @Override\n        public boolean areValidArguments(Object... args) {\n            return args.length == argCount;\n        }\n\n        @Override\n        public String getTranslationKey() {\n            return key;\n        }\n    }\n\n    private enum DireNineByNineTranslation implements ITranslationProvider {\n        ANOTHER_ONE_REALLY(\".another_one\"),\n        DONT_TELL_YOUR_VIEWERS(\".dont_tell\"),\n        YOU_SURE_THATS_THE_CORRECT_SIZE(\".correct_size\"),\n        THIS_TIME_NO_DIRE_WIRE(\".no_dire_wire\");\n        private static final List<ITranslationProvider> VALUES = ImmutableList.copyOf(values());\n        private static final String PREFIX = Reference.MODID + \".easter_eggs.dire_9x9\";\n        private final String key;\n        private final int argCount;\n\n        DireNineByNineTranslation(@Nonnull String key, @Nonnegative int argCount) {\n            this.key = PREFIX + key;\n            this.argCount = argCount;\n        }\n\n        DireNineByNineTranslation(@Nonnull String key) {\n            this(key, 0);\n        }\n\n        @Override\n        public boolean areValidArguments(Object... args) {\n            return args.length == argCount;\n        }\n\n        @Override\n        public String getTranslationKey() {\n            return key;\n        }\n    }\n\n    // even though it's called sizeInvalid to hide it's purpose, it invokes the 9X9 easter egg\n    public static boolean sizeInvalid(Player player, Region region) {\n        BlockPos size = region.getMax().subtract(region.getMin());\n        if (size.getX() == 8 && size.getZ() == 8) { //size is 8 if it's a 9X9\n            List<ITranslationProvider> list = player.getName().getContents().equals(\"Direwolf20\") ? DireNineByNineTranslation.VALUES : NineByNineTranslation.VALUES;\n            int pos = EASTER_RAND.nextInt(list.size());\n            ITranslationProvider provider = list.get(pos);\n            player.displayClientMessage(provider.componentTranslation().setStyle(Styles.DK_GREEN), true);\n            return true;\n        }\n        return false;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/util/CommonUtils.java",
    "content": "package com.direwolf20.buildinggadgets.common.util;\n\nimport com.direwolf20.buildinggadgets.common.tainted.Tainted;\nimport com.direwolf20.buildinggadgets.common.tainted.building.PlacementTarget;\nimport com.direwolf20.buildinggadgets.common.tainted.building.view.BuildContext;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.materials.MaterialList;\nimport net.minecraft.core.Direction;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.world.phys.BlockHitResult;\nimport net.minecraft.world.phys.Vec3;\n\nimport javax.annotation.Nullable;\n\npublic final class CommonUtils {\n    private CommonUtils() {}\n\n    public static BlockHitResult fakeRayTrace(Vec3 simulatePos, BlockPos pos) {\n        Vec3 simVec = Vec3.atLowerCornerOf(pos).subtract(simulatePos);\n        Direction dir = Direction.getNearest(simVec.x(), simVec.y(), simVec.z());\n        return new BlockHitResult(simVec, dir, pos, false);\n    }\n\n    @Tainted(reason = \"Part of the material list system\")\n    public static MaterialList estimateRequiredItems(Iterable<PlacementTarget> buildView, BuildContext context, @Nullable Vec3 simulatePos) {\n        MaterialList.SubEntryBuilder builder = MaterialList.andBuilder();\n        for (PlacementTarget placementTarget : buildView) {\n            BlockHitResult target = simulatePos != null ? CommonUtils.fakeRayTrace(simulatePos, placementTarget.getPos()) : null;\n            builder.add(placementTarget.getRequiredMaterials(context, target));\n        }\n        return builder.build();\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/util/GadgetUtils.java",
    "content": "package com.direwolf20.buildinggadgets.common.util;\n\nimport com.direwolf20.buildinggadgets.common.blocks.EffectBlock;\nimport com.direwolf20.buildinggadgets.common.capability.CapabilityTemplate;\nimport com.direwolf20.buildinggadgets.common.config.Config;\nimport com.direwolf20.buildinggadgets.common.items.AbstractGadget;\nimport com.direwolf20.buildinggadgets.common.items.GadgetBuilding;\nimport com.direwolf20.buildinggadgets.common.items.GadgetExchanger;\nimport com.direwolf20.buildinggadgets.common.items.modes.AbstractMode;\nimport com.direwolf20.buildinggadgets.common.network.packets.PacketRotateMirror;\nimport com.direwolf20.buildinggadgets.common.tainted.building.BlockData;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.InventoryHelper;\nimport com.direwolf20.buildinggadgets.common.tainted.inventory.InventoryLinker;\nimport com.direwolf20.buildinggadgets.common.tainted.template.Template;\nimport com.direwolf20.buildinggadgets.common.tainted.template.TemplateHeader;\nimport com.direwolf20.buildinggadgets.common.tileentities.ConstructionBlockTileEntity;\nimport com.direwolf20.buildinggadgets.common.util.helpers.VectorHelper;\nimport com.direwolf20.buildinggadgets.common.util.lang.MessageTranslation;\nimport com.direwolf20.buildinggadgets.common.util.lang.Styles;\nimport com.direwolf20.buildinggadgets.common.util.lang.TooltipTranslation;\nimport com.direwolf20.buildinggadgets.common.util.ref.NBTKeys;\nimport com.google.common.collect.ImmutableList;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.core.Direction.Axis;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.nbt.ListTag;\nimport net.minecraft.nbt.NbtIo;\nimport net.minecraft.nbt.NbtUtils;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.resources.ResourceLocation;\nimport net.minecraft.util.Mth;\nimport net.minecraft.world.InteractionResult;\nimport net.minecraft.world.InteractionResultHolder;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.level.ClipContext;\nimport net.minecraft.world.level.Level;\nimport net.minecraft.world.level.block.Block;\nimport net.minecraft.world.level.block.Blocks;\nimport net.minecraft.world.level.block.Mirror;\nimport net.minecraft.world.level.block.Rotation;\nimport net.minecraft.world.level.block.entity.BlockEntity;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.phys.BlockHitResult;\nimport net.minecraftforge.common.capabilities.ForgeCapabilities;\nimport net.minecraftforge.common.util.LazyOptional;\nimport net.minecraftforge.items.IItemHandler;\n\nimport javax.annotation.Nonnull;\nimport javax.annotation.Nullable;\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.stream.Collectors;\n\npublic class GadgetUtils {\n    // TODO: migrate to tags\n    private static final ImmutableList<Block> DISALLOWED_BLOCKS = ImmutableList.of(\n            Blocks.END_PORTAL, Blocks.NETHER_PORTAL, Blocks.END_PORTAL_FRAME, Blocks.BEDROCK, Blocks.SPAWNER\n    );\n\n    private static final ImmutableList<String> LINK_STARTS = ImmutableList.of(\"http\", \"www\");\n\n    public static boolean mightBeLink(final String s) {\n        return LINK_STARTS.stream().anyMatch(s::startsWith);\n    }\n\n    public static void addTooltipNameAndAuthor(ItemStack stack, @Nullable Level world, List<Component> tooltip) {\n        if (world != null) {\n            world.getCapability(CapabilityTemplate.TEMPLATE_PROVIDER_CAPABILITY).ifPresent(provider -> {\n                stack.getCapability(CapabilityTemplate.TEMPLATE_KEY_CAPABILITY).ifPresent(key -> {\n                    Template template = provider.getTemplateForKey(key);\n                    TemplateHeader header = template.getHeader();\n                    if (header.getName() != null && !header.getName().isEmpty())\n                        tooltip.add(TooltipTranslation.TEMPLATE_NAME.componentTranslation(header.getName()).setStyle(Styles.AQUA));\n                    if (header.getAuthor() != null && !header.getAuthor().isEmpty())\n                        tooltip.add(TooltipTranslation.TEMPLATE_AUTHOR.componentTranslation(header.getAuthor()).setStyle(Styles.AQUA));\n                });\n            });\n        }\n//        EventTooltip.addTemplatePadding(stack, tooltip);\n    }\n\n    @Nullable\n    public static ByteArrayOutputStream getPasteStream(@Nonnull CompoundTag compound, @Nullable String name) throws IOException {\n        CompoundTag withText = name != null && !name.isEmpty() ? compound.copy() : compound;\n        if (name != null && !name.isEmpty()) withText.putString(NBTKeys.TEMPLATE_NAME, name);\n        ByteArrayOutputStream baos = new ByteArrayOutputStream();\n        NbtIo.writeCompressed(withText, baos);\n        return baos.size() < Short.MAX_VALUE - 200 ? baos : null;\n    }\n\n    public static void setAnchor(ItemStack stack) {\n        setAnchor(stack, new ArrayList<>());\n    }\n\n    public static void setAnchor(ItemStack stack, List<BlockPos> coordinates) {\n        //Store 1 set of BlockPos in NBT to anchor the Ghost Blocks in the world when the anchor key is pressed\n        CompoundTag tagCompound = stack.getOrCreateTag();\n        tagCompound.put(NBTKeys.GADGET_ANCHOR_COORDS, coordinates.stream().map(NbtUtils::writeBlockPos).collect(Collectors.toCollection(ListTag::new)));\n        stack.setTag(tagCompound);\n    }\n\n    public static Optional<List<BlockPos>> getAnchor(ItemStack stack) {\n        //Return the list of coordinates in the NBT Tag for anchor Coordinates\n        CompoundTag tagCompound = stack.getTag();\n        if (tagCompound == null)\n            return Optional.empty();\n\n        ListTag coordList = (ListTag) tagCompound.get(NBTKeys.GADGET_ANCHOR_COORDS);\n        if (coordList == null || coordList.size() == 0)\n            return Optional.empty();\n\n        List<BlockPos> coordinates = new ArrayList<>();\n        for (int i = 0; i < coordList.size(); i++) {\n            coordinates.add(NbtUtils.readBlockPos(coordList.getCompound(i)));\n        }\n\n        return Optional.of(coordinates);\n    }\n\n    public static void setToolRange(ItemStack stack, int range) {\n        //Store the tool's range in NBT as an Integer\n        CompoundTag tagCompound = stack.getOrCreateTag();\n        tagCompound.putInt(\"range\", range);\n    }\n\n    public static int getToolRange(ItemStack stack) {\n        CompoundTag tagCompound = stack.getOrCreateTag();\n        return Mth.clamp(tagCompound.getInt(\"range\"), 1, Config.GADGETS.maxRange.get());\n    }\n\n    public static BlockData rotateOrMirrorBlock(Player player, PacketRotateMirror.Operation operation, BlockData data) {\n        if (operation == PacketRotateMirror.Operation.MIRROR)\n            return data.mirror(player.getDirection().getAxis() == Axis.X ? Mirror.LEFT_RIGHT : Mirror.FRONT_BACK);\n\n        return data.rotate(Rotation.CLOCKWISE_90);\n    }\n\n    public static void rotateOrMirrorToolBlock(ItemStack stack, Player player, PacketRotateMirror.Operation operation) {\n        setToolBlock(stack, rotateOrMirrorBlock(player, operation, getToolBlock(stack)));\n    }\n\n    private static void setToolBlock(ItemStack stack, @Nullable BlockData data) {\n        //Store the selected block in the tool's NBT\n        CompoundTag tagCompound = stack.getOrCreateTag();\n        if (data == null)\n            data = BlockData.AIR;\n\n        CompoundTag stateTag = data.serialize(true);\n        tagCompound.put(NBTKeys.TE_CONSTRUCTION_STATE, stateTag);\n        stack.setTag(tagCompound);\n    }\n\n\n    @Nonnull\n    public static BlockData getToolBlock(ItemStack stack) {\n        CompoundTag tagCompound = stack.getOrCreateTag();\n        BlockData res = BlockData.tryDeserialize(tagCompound.getCompound(NBTKeys.TE_CONSTRUCTION_STATE), true);\n        if (res == null) {\n            setToolBlock(stack, BlockData.AIR);\n            return BlockData.AIR;\n        }\n        return res;\n    }\n\n    public static void linkToInventory(ItemStack stack, Player player) {\n        Level world = player.level;\n        BlockHitResult lookingAt = VectorHelper.getLookingAt(player, AbstractGadget.shouldRayTraceFluid(stack) ? ClipContext.Fluid.ANY : ClipContext.Fluid.NONE);\n        if (world.getBlockState(VectorHelper.getLookingAt(player, stack).getBlockPos()) == Blocks.AIR.defaultBlockState())\n            return;\n\n        InventoryLinker.Result result = InventoryLinker.linkInventory(player.level, stack, lookingAt);\n        player.displayClientMessage(result.getI18n().componentTranslation(), true);\n    }\n\n    public static InteractionResultHolder<Block> selectBlock(ItemStack stack, Player player) {\n        // Used to find which block the player is looking at, and store it in NBT on the tool.\n        Level world = player.level;\n        BlockHitResult lookingAt = VectorHelper.getLookingAt(player, AbstractGadget.shouldRayTraceFluid(stack) ? ClipContext.Fluid.ANY : ClipContext.Fluid.NONE);\n        if (world.isEmptyBlock(lookingAt.getBlockPos()))\n            return InteractionResultHolder.fail(Blocks.AIR);\n\n        BlockState state = world.getBlockState(lookingAt.getBlockPos());\n        if (!((AbstractGadget) stack.getItem()).isAllowedBlock(state) || state.getBlock() instanceof EffectBlock)\n            return InteractionResultHolder.fail(state.getBlock());\n\n        if (DISALLOWED_BLOCKS.contains(state.getBlock())) {\n            return InteractionResultHolder.fail(state.getBlock());\n        }\n\n        if (state.getDestroySpeed(world, lookingAt.getBlockPos()) < 0) {\n            return InteractionResultHolder.fail(state.getBlock());\n        }\n\n        Optional<BlockData> data = InventoryHelper.getSafeBlockData(player, lookingAt.getBlockPos(), player.getUsedItemHand());\n        data.ifPresent(placeState -> {\n            BlockState actualState = placeState.getState();\n            setToolBlock(stack, new BlockData(actualState, placeState.getTileData()));\n        });\n\n        return InteractionResultHolder.success(state.getBlock());\n    }\n\n    public static InteractionResult setRemoteInventory(ItemStack stack, Player player, Level world, BlockPos pos, boolean setTool) {\n        BlockEntity te = world.getBlockEntity(pos);\n        if (te == null)\n            return InteractionResult.PASS;\n\n        if (setTool && te instanceof ConstructionBlockTileEntity) {\n            setToolBlock(stack, ((ConstructionBlockTileEntity) te).getConstructionBlockData());\n            return InteractionResult.SUCCESS;\n        }\n\n\n        return InteractionResult.FAIL;\n    }\n\n    public static boolean anchorBlocks(Player player, ItemStack stack) {\n        //Stores the current visual blocks in NBT on the tool, so the player can look around without moving the visual render\n        Optional<List<BlockPos>> anchorCoords = getAnchor(stack);\n\n        if (anchorCoords.isPresent()) {  //If theres already an anchor, remove it.\n            setAnchor(stack);\n            player.displayClientMessage(MessageTranslation.ANCHOR_REMOVED.componentTranslation().setStyle(Styles.AQUA), true);\n            return true;\n        }\n\n        //If we don't have an anchor, find the block we're supposed to anchor to\n        BlockHitResult lookingAt = VectorHelper.getLookingAt(player, stack);\n        BlockPos startBlock = lookingAt.getBlockPos();\n        Direction sideHit = lookingAt.getDirection();\n\n        //If we aren't looking at anything, exit\n        if (player.level.isEmptyBlock(startBlock))\n            return false;\n\n        BlockData blockData = getToolBlock(stack);\n        AbstractMode.UseContext context = new AbstractMode.UseContext(player.level, player, blockData.getState(), startBlock, stack, sideHit, stack.getItem() instanceof GadgetBuilding && GadgetBuilding.shouldPlaceAtop(stack), stack.getItem() instanceof GadgetBuilding ? GadgetBuilding.getConnectedArea(stack) : GadgetExchanger.getConnectedArea(stack));\n\n        List<BlockPos> coords = stack.getItem() instanceof GadgetBuilding\n                ? GadgetBuilding.getToolMode(stack).getMode().getCollection(context, player)\n                : GadgetExchanger.getToolMode(stack).getMode().getCollection(context, player);\n\n        setAnchor(stack, coords); //Set the anchor NBT\n        player.displayClientMessage(MessageTranslation.ANCHOR_SET.componentTranslation().setStyle(Styles.AQUA), true);\n\n        return true;\n    }\n\n    public static String withSuffix(int count) {\n        if (count < 1000) return \"\" + count;\n        int exp = (int) (Math.log(count) / Math.log(1000));\n        return String.format(\"%.1f%c\",\n                count / Math.pow(1000, exp),\n                \"kMGTPE\".charAt(exp - 1));\n    }\n\n    public static void writePOSToNBT(ItemStack stack, @Nullable BlockPos pos, String tagName) {\n        CompoundTag tagCompound = stack.getOrCreateTag();\n\n        if (pos == null) {\n            if (tagCompound.get(tagName) != null) {\n                tagCompound.remove(tagName);\n                stack.setTag(tagCompound);\n            }\n            return;\n        }\n        tagCompound.put(tagName, NbtUtils.writeBlockPos(pos));\n        stack.setTag(tagCompound);\n    }\n\n\n    @Nullable\n    public static BlockPos getPOSFromNBT(ItemStack stack, String tagName) {\n        CompoundTag stackTag = stack.getOrCreateTag();\n        if (!stackTag.contains(tagName))\n            return null;\n        CompoundTag posTag = stack.getOrCreateTag().getCompound(tagName);\n        if (posTag.isEmpty())\n            return null;\n        return NbtUtils.readBlockPos(posTag);\n    }\n\n\n    @Nullable\n    public static ResourceLocation getDIMFromNBT(ItemStack stack, String tagName) {\n        CompoundTag tagCompound = stack.getTag();\n        if (tagCompound == null) {\n            return null;\n        }\n        CompoundTag posTag = tagCompound.getCompound(tagName);\n        if (posTag.equals(new CompoundTag())) {\n            return null;\n        }\n        return new ResourceLocation(posTag.getString(NBTKeys.GADGET_DIM));\n    }\n\n    /**\n     * Drops the IItemHandlerModifiable Inventory of the TileEntity at the specified position.\n     */\n    public static void dropTileEntityInventory(Level world, BlockPos pos) {\n        BlockEntity tileEntity = world.getBlockEntity(pos);\n        if (tileEntity != null) {\n            LazyOptional<IItemHandler> cap = tileEntity.getCapability(ForgeCapabilities.ITEM_HANDLER);\n            cap.ifPresent(handler -> {\n                for (int i = 0; i < handler.getSlots(); i++) {\n                    net.minecraft.world.Containers.dropItemStack(world, pos.getX(), pos.getY(), pos.getZ(), handler.getStackInSlot(i));\n                }\n            });\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/util/compression/DataCompressor.java",
    "content": "package com.direwolf20.buildinggadgets.common.util.compression;\n\nimport it.unimi.dsi.fastutil.objects.Object2IntMap;\nimport it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;\nimport net.minecraft.nbt.Tag;\nimport net.minecraft.nbt.ListTag;\n\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.function.Function;\nimport java.util.function.ToIntFunction;\n\n/**\n * Allows a simple nbt-data compression to happen, by building a map from the Data to int's.\n * Every call to {@link #applyAsInt(Object)} will return the int key associated with the given value,\n * which is evaluated if the value is at that point unknown. This int key can then be used to efficiently\n * represent the value, which will obviously reduce the size for duplicate values.\n * <p>\n * The mapping build can be represented efficiently using a {@link ListNBT NBTList}. For reading this\n * and reversing the mapping use {@link DataDecompressor}.\n * <p>\n * This class implements {@link ToIntFunction} for cases, where another int mapping may be used sometimes.\n * For example if the Data is associated with an {@link net.minecraftforge.registries.IForgeRegistry} and the\n * integer id's might be used, it is of course more efficient to use those directly.\n *\n * @param <T> The type of Data which is compressed\n * @see DataDecompressor\n */\npublic final class DataCompressor<T> implements ToIntFunction<T> {\n    private int cur;\n    private Object2IntMap<T> objectMap;\n    private List<T> reverseMap;\n\n    public DataCompressor(int expectedSize) {\n        cur = 0;\n        objectMap = new Object2IntOpenHashMap<>(expectedSize);\n        reverseMap = new LinkedList<>();\n    }\n\n    public DataCompressor() {\n        cur = 0;\n        objectMap = new Object2IntOpenHashMap<>();\n        reverseMap = new LinkedList<>();\n    }\n\n    @Override\n    public int applyAsInt(T value) {\n        if (objectMap.containsKey(value))\n            return objectMap.getInt(value);\n        objectMap.put(value, cur);\n        reverseMap.add(value);\n        return cur++;\n    }\n\n    public List<T> getReverseMap() {\n        return reverseMap;\n    }\n\n    public ListTag write(Function<T, ? extends Tag> serializer) {\n        ListTag resList = new ListTag();\n        for (T entry : reverseMap) {\n            resList.add(serializer.apply(entry));\n        }\n        return resList;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/util/compression/DataDecompressor.java",
    "content": "package com.direwolf20.buildinggadgets.common.util.compression;\n\nimport it.unimi.dsi.fastutil.ints.Int2ObjectMap;\nimport it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;\nimport net.minecraft.nbt.Tag;\nimport net.minecraft.nbt.ListTag;\n\nimport java.util.function.Function;\nimport java.util.function.IntFunction;\n\n/**\n * Allows to read data compressed to integers by {@link DataCompressor}.\n * Every call to {@link #apply(int)} will lookup the appropriate value from the compressed data which\n * was read in the constructor.\n * <p>\n * This class implements {@link IntFunction} for cases, where another int mapping may be used sometimes.\n * For example if the Data is associated with an {@link net.minecraftforge.registries.IForgeRegistry} and the\n * integer id's might be used, it is of course more efficient to use those directly.\n *\n * @param <T> The type of Data to decompress\n * @see DataCompressor\n */\npublic class DataDecompressor<T> implements IntFunction<T> {\n    private Int2ObjectMap<T> int2ObjectMap;\n    private IntFunction<T> defaultFun;\n\n    public DataDecompressor(ListTag list, Function<Tag, T> deserializer, IntFunction<T> defaultFun) {\n        int2ObjectMap = new Int2ObjectOpenHashMap<>(list.size());\n        this.defaultFun = defaultFun;\n        int count = 0;\n        for (Tag entry : list) {\n            int2ObjectMap.put(count++, deserializer.apply(entry));\n        }\n    }\n\n    @Override\n    public T apply(int value) {\n        if (! int2ObjectMap.containsKey(value))\n            return defaultFun.apply(value);\n        return int2ObjectMap.get(value);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/util/compression/package-info.java",
    "content": "@Tainted(reason = \"Unneeded\")\npackage com.direwolf20.buildinggadgets.common.util.compression;\n\nimport com.direwolf20.buildinggadgets.common.tainted.Tainted;"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/util/exceptions/CapabilityNotPresentException.java",
    "content": "package com.direwolf20.buildinggadgets.common.util.exceptions;\n\npublic class CapabilityNotPresentException extends IllegalStateException {\n\n    public CapabilityNotPresentException() {\n        super(\"Capability was not present and therefore could not be queried. No further Information available!\");\n    }\n\n    public CapabilityNotPresentException(Throwable cause) {\n        super(\"Capability was not present and therefore could not be queried. No further Information available!\", cause);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/util/exceptions/TemplateParseException.java",
    "content": "package com.direwolf20.buildinggadgets.common.util.exceptions;\n\nimport com.google.gson.JsonParseException;\n\npublic class TemplateParseException extends JsonParseException {\n\n    public TemplateParseException(String msg) {\n        super(msg);\n    }\n\n    public TemplateParseException(String msg, Throwable cause) {\n        super(msg, cause);\n    }\n\n    public static final class IllegalMinecraftVersionException extends TemplateParseException {\n        private final String minecraftVersion;\n\n        public IllegalMinecraftVersionException(String minecraftVersion) {\n            super(\"Attempted to load Template for illegal minecraft version \" + minecraftVersion + \"!\");\n            this.minecraftVersion = minecraftVersion;\n        }\n\n        public IllegalMinecraftVersionException(Throwable cause, String minecraftVersion) {\n            super(\"Attempted to load Template for illegal minecraft version \" + minecraftVersion + \"!\", cause);\n            this.minecraftVersion = minecraftVersion;\n        }\n\n        public String getMinecraftVersion() {\n            return minecraftVersion;\n        }\n    }\n\n    public static final class UnknownTemplateVersionException extends TemplateParseException {\n        private final String templateVersion;\n\n        public UnknownTemplateVersionException(String templateVersion) {\n            super(\"Attempted to load Template with too recent (unknown) format (version=\" + templateVersion + \")!\");\n            this.templateVersion = templateVersion;\n        }\n\n        public UnknownTemplateVersionException(Throwable cause, String templateVersion) {\n            super(\"Attempted to load Template with too recent (unknown) format (version=\" + templateVersion + \")!\", cause);\n            this.templateVersion = templateVersion;\n        }\n\n        public String getTemplateVersion() {\n            return templateVersion;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/util/exceptions/TemplateReadException.java",
    "content": "package com.direwolf20.buildinggadgets.common.util.exceptions;\n\nimport net.minecraft.nbt.CompoundTag;\n\npublic class TemplateReadException extends Exception {\n    public TemplateReadException() {\n    }\n\n    public TemplateReadException(String message) {\n        super(message);\n    }\n\n    public TemplateReadException(String message, Throwable cause) {\n        super(message, cause);\n    }\n\n    public TemplateReadException(Throwable cause) {\n        super(cause);\n    }\n\n    public TemplateReadException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {\n        super(message, cause, enableSuppression, writableStackTrace);\n    }\n\n    public static final class CorruptJsonException extends TemplateReadException {\n        public CorruptJsonException() {\n            super(\"Failed to read Template, because given copy does not constitute a valid Template-json.\");\n        }\n\n        public CorruptJsonException(Throwable cause) {\n            super(\"Failed to read Template, because given copy does not constitute a valid Template-json.\", cause);\n        }\n    }\n\n    public static final class CorruptDataException extends TemplateReadException {\n        private final String templateData;\n\n        public CorruptDataException(String templateData) {\n            super(\"Could not interpret Template body as a valid Template.\");\n            this.templateData = templateData;\n        }\n\n        public CorruptDataException(Throwable cause, String templateData) {\n            super(\"Could not interpret Template body as a valid Template.\", cause);\n            this.templateData = templateData;\n        }\n\n        public String getTemplateData() {\n            return templateData;\n        }\n    }\n\n    public static class DataCannotBeReadException extends TemplateReadException {\n        public DataCannotBeReadException(Throwable cause) {\n            super(\"Unable to read TemplateItem nbt from Stream!\", cause);\n        }\n    }\n\n    public static class IllegalNBTDataException extends TemplateReadException {\n        private final CompoundTag nbt;\n\n        public IllegalNBTDataException(CompoundTag nbt) {\n            super(\"Could not read nbt data format.\");\n            this.nbt = nbt;\n        }\n\n        public IllegalNBTDataException(Throwable cause, CompoundTag nbt) {\n            super(\"Could not read nbt data format.\", cause);\n            this.nbt = nbt;\n        }\n\n        public CompoundTag getNbt() {\n            return nbt;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/util/exceptions/TemplateWriteException.java",
    "content": "package com.direwolf20.buildinggadgets.common.util.exceptions;\n\nimport net.minecraft.nbt.CompoundTag;\n\npublic class TemplateWriteException extends Exception {\n    public TemplateWriteException() {\n    }\n\n    public TemplateWriteException(String message) {\n        super(message);\n    }\n\n    public TemplateWriteException(String message, Throwable cause) {\n        super(message, cause);\n    }\n\n    public TemplateWriteException(Throwable cause) {\n        super(cause);\n    }\n\n    public TemplateWriteException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {\n        super(message, cause, enableSuppression, writableStackTrace);\n    }\n\n    public static final class DataCannotBeWrittenException extends TemplateWriteException {\n        private final CompoundTag nbt;\n\n        public DataCannotBeWrittenException(Throwable cause, CompoundTag nbt) {\n            super(\"Unable to write TemplateItem data to bytes!\", cause);\n            this.nbt = nbt;\n        }\n\n        public CompoundTag getNbt() {\n            return nbt;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/util/exceptions/package-info.java",
    "content": "@Tainted(reason = \"Used fail first logic. None of these should be used nor implemented.\")\npackage com.direwolf20.buildinggadgets.common.util.exceptions;\n\nimport com.direwolf20.buildinggadgets.common.tainted.Tainted;"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/util/helpers/NBTHelper.java",
    "content": "package com.direwolf20.buildinggadgets.common.util.helpers;\n\nimport com.direwolf20.buildinggadgets.common.tainted.Tainted;\nimport com.direwolf20.buildinggadgets.common.util.ref.NBTKeys;\nimport com.google.common.collect.Multiset;\nimport net.minecraft.nbt.CompoundTag;\nimport net.minecraft.nbt.Tag;\nimport net.minecraft.nbt.ListTag;\nimport net.minecraft.util.Tuple;\n\nimport java.util.*;\nimport java.util.function.BiConsumer;\nimport java.util.function.BinaryOperator;\nimport java.util.function.Function;\nimport java.util.function.Supplier;\nimport java.util.stream.Collector;\nimport java.util.stream.StreamSupport;\n\nimport java.util.stream.Collector.Characteristics;\n\n/**\n * Utility class providing additional Methods for reading and writing array's which are not normally provided as\n * NBT-Objects from Minecraft.\n */\n\n@Tainted(reason = \"Everything here is single use. It should not be a helper\")\npublic class NBTHelper {\n    public static <T> ListTag writeIterable(Iterable<T> iterable, Function<? super T, ? extends Tag> serializer) {\n        return StreamSupport.stream(iterable.spliterator(), false).map(serializer).collect(toListNBT());\n    }\n\n    public static <K, V> ListTag serializeMap(Map<K, V> map, Function<? super K, ? extends Tag> keySerializer, Function<? super V, ? extends Tag> valueSerializer) {\n        ListTag list = new ListTag();\n        for (Map.Entry<K, V> entry : map.entrySet()) {\n            CompoundTag compound = new CompoundTag();\n            compound.put(NBTKeys.MAP_SERIALIZE_KEY, keySerializer.apply(entry.getKey()));\n            compound.put(NBTKeys.MAP_SERIALIZE_VALUE, valueSerializer.apply(entry.getValue()));\n            list.add(compound);\n        }\n        return list;\n    }\n\n    public static <V> ListTag serializeUUIDMap(Map<UUID, V> map, Function<? super V, ? extends Tag> valueSerializer) {\n        ListTag list = new ListTag();\n        for (Map.Entry<UUID, V> entry : map.entrySet()) {\n            CompoundTag compound = new CompoundTag();\n            compound.putUUID(NBTKeys.MAP_SERIALIZE_KEY, entry.getKey());\n            compound.put(NBTKeys.MAP_SERIALIZE_VALUE, valueSerializer.apply(entry.getValue()));\n            list.add(compound);\n        }\n        return list;\n    }\n\n    public static <K, V> Map<K, V> deserializeMap(ListTag list, Map<K, V> toAppendTo, Function<Tag, ? extends K> keyDeserializer, Function<Tag, ? extends V> valueDeserializer) {\n        for (Tag nbt : list) {\n            if (nbt instanceof CompoundTag) {\n                CompoundTag compound = (CompoundTag) nbt;\n                toAppendTo.put(\n                        keyDeserializer.apply(compound.get(NBTKeys.MAP_SERIALIZE_KEY)),\n                        valueDeserializer.apply(compound.get(NBTKeys.MAP_SERIALIZE_VALUE))\n                );\n            }\n        }\n        return toAppendTo;\n    }\n\n    public static <V> Map<UUID, V> deserializeUUIDMap(ListTag list, Map<UUID, V> toAppendTo, Function<Tag, ? extends V> valueDeserializer) {\n        for (Tag nbt : list) {\n            if (nbt instanceof CompoundTag) {\n                CompoundTag compound = (CompoundTag) nbt;\n                toAppendTo.put(\n                        compound.getUUID(NBTKeys.MAP_SERIALIZE_KEY),\n                        valueDeserializer.apply(compound.get(NBTKeys.MAP_SERIALIZE_VALUE))\n                );\n            }\n        }\n        return toAppendTo;\n    }\n\n    public static <T, C extends Collection<T>> C deserializeCollection(ListTag list, C toAppendTo, Function<Tag, ? extends T> elementDeserializer) {\n        for (Tag nbt : list) {\n            toAppendTo.add(elementDeserializer.apply(nbt));\n        }\n        return toAppendTo;\n    }\n\n    public static <T> Multiset<T> deserializeMultisetEntries(ListTag list, Multiset<T> toAppendTo, Function<Tag, Tuple<? extends T, Integer>> entryDeserializer) {\n        list.stream().map(entryDeserializer).forEach(p -> toAppendTo.add(p.getA(), p.getB()));\n        return toAppendTo;\n    }\n\n    public static <T extends Tag> Collector<T, ListTag, ListTag> toListNBT() {\n        return new Collector<T, ListTag, ListTag>() {\n            @Override\n            public Supplier<ListTag> supplier() {\n                return ListTag::new;\n            }\n\n            @Override\n            public BiConsumer<ListTag, T> accumulator() {\n                return ListTag::add;\n            }\n\n            @Override\n            public BinaryOperator<ListTag> combiner() {\n                return (l1, l2) -> {\n                    l1.addAll(l2);\n                    return l1;\n                };\n            }\n\n            @Override\n            public Function<ListTag, ListTag> finisher() {\n                return Function.identity();\n            }\n\n            @Override\n            public Set<Characteristics> characteristics() {\n                return EnumSet.of(Characteristics.IDENTITY_FINISH, Characteristics.UNORDERED);\n            }\n        };\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/util/helpers/VectorHelper.java",
    "content": "package com.direwolf20.buildinggadgets.common.util.helpers;\n\nimport com.direwolf20.buildinggadgets.common.config.Config;\nimport com.direwolf20.buildinggadgets.common.items.AbstractGadget;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraft.world.phys.BlockHitResult;\nimport net.minecraft.world.level.ClipContext;\nimport net.minecraft.world.phys.HitResult;\n\npublic class VectorHelper {\n\n    public static BlockHitResult getLookingAt(Player player, ItemStack tool) {\n        return getLookingAt(player, AbstractGadget.shouldRayTraceFluid(tool) ? ClipContext.Fluid.ANY : ClipContext.Fluid.NONE);\n    }\n\n    public static BlockHitResult getLookingAt(Player player, boolean shouldRayTrace) {\n        return getLookingAt(player, shouldRayTrace ? ClipContext.Fluid.ANY : ClipContext.Fluid.NONE);\n    }\n\n    public static BlockHitResult getLookingAt(Player player, ClipContext.Fluid rayTraceFluid) {\n        double rayTraceRange = Config.GENERAL.rayTraceRange.get();\n        HitResult result = player.pick(rayTraceRange, 0f, rayTraceFluid != ClipContext.Fluid.NONE);\n\n        return (BlockHitResult) result;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/util/lang/CommandTranslation.java",
    "content": "package com.direwolf20.buildinggadgets.common.util.lang;\n\nimport com.direwolf20.buildinggadgets.common.util.ref.Reference;\n\nimport javax.annotation.Nonnegative;\nimport javax.annotation.Nonnull;\n\npublic enum CommandTranslation implements ITranslationProvider {\n    FORCE_UNLOADED_NO_PLAYER(\"force_unloaded.no_player\", 0),\n    FORCE_UNLOADED_TOGGLED(\"force_unloaded.toggled\", 2),\n    FORCE_UNLOADED_LIST(\"force_unloaded.list\", 2),\n    OVERRIDE_COPY_SIZE_NO_PLAYER(\"override_copy_size.no_player\", 0),\n    OVERRIDE_COPY_SIZE_TOGGLED(\"override_copy_size.toggled\", 2),\n    OVERRIDE_COPY_SIZE_LIST(\"override_copy_size.list\", 2),\n    OVERRIDE_BUILD_SIZE_NO_PLAYER(\"override_build_size.no_player\", 0),\n    OVERRIDE_BUILD_SIZE_TOGGLED(\"override_build_size.toggled\", 2),\n    OVERRIDE_BUILD_SIZE_LIST(\"override_build_size.list\", 2);\n    private static final String PREFIX = Reference.MODID + \".commands.\";\n    private final String key;\n    private final int argCount;\n\n    CommandTranslation(@Nonnull String key, @Nonnegative int argCount) {\n        this.key = PREFIX + key;\n        this.argCount = argCount;\n    }\n\n    @Override\n    public boolean areValidArguments(Object... args) {\n        return args.length == argCount;\n    }\n\n    @Override\n    public String getTranslationKey() {\n        return key;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/util/lang/GuiTranslation.java",
    "content": "package com.direwolf20.buildinggadgets.common.util.lang;\n\nimport com.direwolf20.buildinggadgets.common.util.ref.Reference;\n\nimport javax.annotation.Nonnull;\n\npublic enum GuiTranslation implements ITranslationProvider {\n    SINGLE_CONFIRM(\"single.confirm\"),\n    SINGLE_CANCEL(\"single.cancel\"),\n    SINGLE_CLOSE(\"single.close\"),\n    SINGLE_CLEAR(\"single.clear\"),\n    SINGLE_RESET(\"single.reset\"),\n    SINGLE_LEFT(\"destruction.field.left\"),\n    SINGLE_RIGHT(\"destruction.field.right\"),\n    SINGLE_UP(\"destruction.field.up\"),\n    SINGLE_DOWN(\"destruction.field.down\"),\n    SINGLE_DEPTH(\"destruction.field.depth\"),\n    SINGLE_RANGE(\"single.range\"),\n\n    BUTTON_LOAD(\"tm.button.load\"),\n    BUTTON_SAVE(\"tm.button.save\"),\n    BUTTON_COPY(\"tm.button.copy\"),\n    BUTTON_PASTE(\"tm.button.paste\"),\n    TEMPLATE_NAME_TIP(\"tm.name_field.text\"),\n    TEMPLATE_PLACEHOLDER(\"tm.field.placeholder\"),\n\n    COPY_BUTTON_ABSOLUTE(\"copy.button.absolute\"),\n    COPY_LABEL_HEADING(\"copy.label.heading\"),\n    COPY_LABEL_SUBHEADING(\"copy.label.subheading\"),\n\n    FIELD_START(\"field.start\"),\n    FIELD_END(\"field.end\");\n\n    private static final String PREFIX = \"gui.\" + Reference.MODID + \".\";\n    private final String key;\n    private final int argCount;\n\n    GuiTranslation(@Nonnull String key) {\n        this.key = PREFIX + key;\n        this.argCount = 0;\n    }\n\n    @Override\n    public boolean areValidArguments(Object... args) {\n        return args.length == argCount;\n    }\n\n    @Override\n    public String getTranslationKey() {\n        return key;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/util/lang/ITranslationProvider.java",
    "content": "package com.direwolf20.buildinggadgets.common.util.lang;\n\nimport net.minecraft.client.resources.language.I18n;\nimport net.minecraft.network.chat.Component;\nimport net.minecraft.network.chat.MutableComponent;\n\npublic interface ITranslationProvider {\n    /*Client side only! */\n    default String format(Object... args) {\n        assert areValidArguments(args);\n        return I18n.get(getTranslationKey(), args);\n    }\n\n    default MutableComponent componentTranslation(Object... args) {\n        assert areValidArguments(args);\n        return Component.translatable(getTranslationKey(), args);\n    }\n\n    boolean areValidArguments(Object... args);\n\n    String getTranslationKey();\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/util/lang/LangUtil.java",
    "content": "package com.direwolf20.buildinggadgets.common.util.lang;\n\nimport com.direwolf20.buildinggadgets.common.util.ref.Reference;\nimport net.minecraft.world.level.block.Block;\nimport net.minecraft.world.level.block.state.BlockState;\n\npublic final class LangUtil {\n    private LangUtil() {}\n\n    public static String getLangKey(String type, String... args) {\n        return String.join(\".\", type, Reference.MODID, String.join(\".\", args));\n    }\n\n    public static String getFormattedBlockName(BlockState block) {\n        return getFormattedBlockName(block.getBlock());\n    }\n\n    public static String getFormattedBlockName(Block block) {\n        return block.getName().getString();\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/util/lang/MaterialListTranslation.java",
    "content": "package com.direwolf20.buildinggadgets.common.util.lang;\n\nimport com.direwolf20.buildinggadgets.common.util.ref.Reference;\n\nimport javax.annotation.Nonnegative;\nimport javax.annotation.Nonnull;\n\npublic enum MaterialListTranslation implements ITranslationProvider {\n    BUTTON_CLOSE(\"button.close\", 0),\n    BUTTON_COPY(\"button.copyList\", 0),\n    BUTTON_SORTING_NAMEAZ(\"button.sorting.nameAZ\", 0),\n    BUTTON_SORTING_NAMEZA(\"button.sorting.nameZA\", 0),\n    BUTTON_SORTING_REQUIREDACSE(\"button.sorting.requiredAcse\", 0),\n    BUTTON_SORTING_REQUIREDESC(\"button.sorting.requiredDesc\", 0),\n    BUTTON_SORTING_MISSINGACSE(\"button.sorting.missingAcse\", 0),\n    BUTTON_SORTING_MISSINGDESC(\"button.sorting.missingDesc\", 0),\n    MESSAGE_COPY_SUCCESS(\"message.copyList.success\", 0),\n    HELP_COPY_LIST(\"help.copyList\", 0),\n    TITLE_EMPTY(\"titleEmpty\", 0),\n    TITLE_AUTHOR_ONLY(\"titleAuthorOnly\", 1),\n    TITLE_NAME_ONLY(\"titleNameOnly\", 1),\n    TITLE(\"title\", 2);\n\n    private static final String PREFIX = \"gui.\" + Reference.MODID + \".materialList.\";\n    private final String key;\n    private final int argCount;\n\n    MaterialListTranslation(@Nonnull String key, @Nonnegative int argCount) {\n        this.key = PREFIX + key;\n        this.argCount = argCount;\n    }\n\n    @Override\n    public boolean areValidArguments(Object... args) {\n        return args.length == argCount;\n    }\n\n    @Override\n    public String getTranslationKey() {\n        return key;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/util/lang/MessageTranslation.java",
    "content": "package com.direwolf20.buildinggadgets.common.util.lang;\n\nimport com.direwolf20.buildinggadgets.common.util.ref.Reference;\n\nimport javax.annotation.Nonnegative;\nimport javax.annotation.Nonnull;\n\npublic enum MessageTranslation implements ITranslationProvider {\n    AREA_COPIED(\"copied\"),\n    AREA_COPIED_FAILED(\"not_copied\"),\n    AREA_COPIED_FAILED_TOO_BIG(\"area_too_big\"),\n    AREA_COPIED_FAILED_TOO_MANY(\"too_many_blocks\"),\n    AREA_COPIED_FAILED_TOO_MANY_DIFF(\"too_many_dif_blocks\"),\n    AREA_RESET(\"area_reset\"),\n    ANCHOR_REMOVED(\"anchor_removed\"),\n    ANCHOR_SET(\"anchor_set\"),\n    BUILD_UNLOADED(\"build_unloaded\", 1),\n    BUILD_TOO_LARGE(\"build_too_large\", 6),\n    CLIPBOARD_COPY_SUCCESS(\"copy_clipboard_success\", 0),\n    CLIPBOARD_COPY_ERROR(\"copy_failed.error\", 0),\n    CLIPBOARD_COPY_ERROR_TEMPLATE(\"copy_failed.template_write\", 0),\n    CONNECTED_AREA(\"connected_area\", 1),\n    CONNECTED_SURFACE(\"connected_surface\", 1),\n    COPY_UNLOADED(\"copy_unloaded\", 1),\n    COPY_TOO_LARGE(\"copy_too_large\", 6),\n    DESTRCUT_TOO_LARGE(\"destroy_size_too_large\"),\n    FIRST_COPY(\"first_copy\"),\n    FUZZY_MODE(\"fuzzy_mode\", 1),\n    GADGET_BUSY(\"gadget_busy\"),\n    INVALID_BLOCK(\"invalid_block\", 1),\n    MIRRORED(\"mirrored\"),\n    MODE_SET(\"tool_mode\", 1),\n    NOTHING_TO_UNDO(\"nothing_to_undo\"),\n    PASTE_FAILED(\"paste_failed\", 0),\n    PASTE_FAILED_LINK_COPIED(\"paste_failed.link_copied\", 0),\n    PASTE_FAILED_WRONG_MC_VERSION(\"paste_failed.wrong_mc_version\", 3),\n    PASTE_FAILED_TOO_RECENT_VERSION(\"paste_failed.too_recent_version\", 2),\n    PASTE_FAILED_CORRUPT_JSON(\"paste_failed.corrupt_json\", 0),\n    PASTE_FAILED_INVALID_JSON(\"paste_failed.invalid_json\", 0),\n    PASTE_FAILED_CORRUPT_BODY(\"paste_failed.corrupt_body\", 0),\n    PASTE_SUCCESS(\"paste_success\", 0),\n    PLACE_ATOP(\"place.atop\"),\n    PLACE_INSIDE(\"place.inside\"),\n    RAYTRACE_FLUID(\"raytrace_fluid\", 1),\n    ROTATED(\"rotated\"),\n    TEMPLATE_BUILD(\"template_build\"),\n    TRANSACTION_FAILED(\"transaction_failed\"),\n    SERVER_BUSY(\"server_busy\"),\n    RANGE_SET(\"range_set\", 1),\n    UNDO_FAILED(\"undo_failed\"),\n    UNDO_UNLOADED(\"undo_unloaded\", 1),\n    UNDO_MISSING_ITEMS(\"undo_missing_items\"),\n    BOUND_TO_TILE(\"boundTE\"),\n    UNBOUND_TO_TILE(\"unboundTE\"),\n    FAILED_TO_BIND_TILE(\"failed_to_bind\"),\n    INVALID_BOUND_TILE(\"invalid_inventory\");\n\n    private static final String PREFIX = Reference.MODID + \".message.\";\n    private final String key;\n    private final int argCount;\n\n    MessageTranslation(@Nonnull String key) {\n        this(key, 0);\n    }\n\n    MessageTranslation(@Nonnull String key, @Nonnegative int argCount) {\n        this.key = PREFIX + key;\n        this.argCount = argCount;\n    }\n\n    @Override\n    public boolean areValidArguments(Object... args) {\n        return args.length == argCount;\n    }\n\n    @Override\n    public String getTranslationKey() {\n        return key;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/util/lang/ModeTranslation.java",
    "content": "package com.direwolf20.buildinggadgets.common.util.lang;\n\nimport com.direwolf20.buildinggadgets.common.util.ref.Reference;\n\nimport javax.annotation.Nonnull;\n\npublic enum ModeTranslation implements ITranslationProvider{\n    COPY(\"copy\"),\n    PASTE(\"paste\");\n\n    private static final String PREFIX = Reference.MODID + \".modes.\";\n    private final String key;\n\n    ModeTranslation(@Nonnull String key) {\n        this.key = PREFIX + key;\n    }\n\n    @Override\n    public boolean areValidArguments(Object... args) {\n        return true;\n    }\n\n    @Override\n    public String getTranslationKey() {\n        return key;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/util/lang/RadialTranslation.java",
    "content": "package com.direwolf20.buildinggadgets.common.util.lang;\n\nimport net.minecraft.client.resources.language.I18n;\n\npublic enum RadialTranslation implements ITranslationProvider {\n    DESTRUCTION_OVERLAY(\"destruction_overlay\"),\n    FLUID_ONLY(\"fluid_only\"),\n    ROTATE(\"rotate\"),\n    MIRROR(\"mirror\"),\n    FUZZY(\"fuzzy\"),\n    CONNECTED_AREA(\"connected_area\"),\n    CONNECTED_SURFACE(\"connected_surface\"),\n    OPEN_GUI(\"open_gui\"),\n    OPEN_MATERIAL_LIST(\"open_material-list\"),\n    RAYTRACE_FLUID(\"raytrace_fluid\"),\n    PLACE_ON_TOP(\"place_on_top\"),\n    ANCHOR(\"anchor\"),\n    UNDO(\"undo\");\n\n    private final String key;\n\n    RadialTranslation(String key) {\n        this.key = \"buildinggadgets.radialmenu.\" + key;\n    }\n\n    @Override\n    public boolean areValidArguments(Object... args) {\n        return true;\n    }\n\n    @Override\n    public String getTranslationKey() {\n        return key;\n    }\n\n    public String getString() {\n        return I18n.get(key);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/util/lang/Styles.java",
    "content": "package com.direwolf20.buildinggadgets.common.util.lang;\n\nimport net.minecraft.network.chat.Style;\nimport net.minecraft.ChatFormatting;\n\npublic final class Styles {\n    private Styles() {}\n\n    public static final Style DK_GREEN = Style.EMPTY.applyFormat(ChatFormatting.DARK_GREEN);\n    public static final Style AQUA = Style.EMPTY.applyFormat(ChatFormatting.AQUA);\n    public static final Style LT_PURPLE = Style.EMPTY.applyFormat(ChatFormatting.LIGHT_PURPLE);\n    public static final Style GOLD = Style.EMPTY.applyFormat(ChatFormatting.GOLD);\n    public static final Style RED = Style.EMPTY.applyFormat(ChatFormatting.RED);\n    public static final Style WHITE = Style.EMPTY.applyFormat(ChatFormatting.WHITE);\n    public static final Style BLUE = Style.EMPTY.applyFormat(ChatFormatting.BLUE);\n    public static final Style YELLOW = Style.EMPTY.applyFormat(ChatFormatting.YELLOW);\n    public static final Style GRAY = Style.EMPTY.applyFormat(ChatFormatting.GRAY);\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/util/lang/TooltipTranslation.java",
    "content": "package com.direwolf20.buildinggadgets.common.util.lang;\n\nimport javax.annotation.Nonnegative;\nimport javax.annotation.Nonnull;\n\npublic enum TooltipTranslation implements ITranslationProvider {\n    CONSTRUCTIONBLOCKPOWDER_HELPTEXT(\"constructionblockpowder.helptext\", 0),\n    GADGET_BLOCK(\"gadget.block\", 1),\n    GADGET_DESTROYSHOWOVERLAY(\"gadget.destroyshowoverlay\", 1),\n    GADGET_DESTROYWARNING(\"gadget.destroywarning\", 0),\n    GADGET_ENERGY(\"gadget.energy\", 2),\n    GADGET_MODE(\"gadget.mode\", 1),\n    GADGET_RANGE(\"gadget.range\", 2),\n    GADGET_FUZZY(\"gadget.fuzzy\", 1),\n    GADGET_RAYTRACE_FLUID(\"gadget.raytrace_fluid\", 1),\n    GADGET_BUILDING_PLACE_ATOP(\"gadget.building.place_atop\", 1),\n    GADGET_CONNECTED(\"gadget.connected\", 1),\n    GADGET_CONNECTED_AREA(\"gadget.connected_area\", 0),\n    GAGDGET_CONNECTED_SURFACE(\"gadget.connected_surface\", 0),\n    GADGET_MIRROR(\"gadget.mirror\", 0),\n    GADGET_ANCHOR(\"gadget.anchor\", 0),\n    GADGET_UNDO(\"gadget.undo\", 0),\n    GADGET_ROTATE(\"gadget.rotate\", 0),\n    GADGET_PALETTE_OVERFLOW(\"gadget.paletteOverflow\", 0),\n    PASTECONTAINER_AMOUNT(\"pasteContainer.amount\", 2),\n    PASTECONTAINER_CREATIVE_AMOUNT(\"pasteContainer.creative.amountMsg\", 0),\n    DONOTUSE_TEXT(\"donotuse\", 0),\n    TEMPLATE_NAME(\"template.name\", 1),\n    TEMPLATE_AUTHOR(\"template.author\", 1),\n    CHARGER_ENERGY(\"charger.energy\", 1),\n    CHARGER_BURN(\"charger.burn_time\", 1),\n    CHARGER_EMPTY(\"charger.fuel_empty\", 0);\n\n    private static final String PREFIX = \"tooltip.\";\n    private final String key;\n    private final int argCount;\n\n    TooltipTranslation(@Nonnull String key, @Nonnegative int argCount) {\n        this.key = PREFIX + key;\n        this.argCount = argCount;\n    }\n\n    @Override\n    public boolean areValidArguments(Object... args) {\n        return args.length == argCount;\n    }\n\n    @Override\n    public String getTranslationKey() {\n        return key;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/util/lang/package-info.java",
    "content": "@Tainted(reason = \"Pointless system introduced to fix programmer error but introduced more steps to an already length process\")\npackage com.direwolf20.buildinggadgets.common.util.lang;\n\nimport com.direwolf20.buildinggadgets.common.tainted.Tainted;"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/util/package-info.java",
    "content": "@ParametersAreNonnullByDefault\n@MethodsReturnNonnullByDefault\npackage com.direwolf20.buildinggadgets.common.util;\n\nimport net.minecraft.MethodsReturnNonnullByDefault;\n\nimport javax.annotation.ParametersAreNonnullByDefault;"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/util/ref/JsonKeys.java",
    "content": "package com.direwolf20.buildinggadgets.common.util.ref;\n\npublic final class JsonKeys {\n    public static final String MATERIAL_LIST_ITEM_NAME = \"name\";\n    public static final String MATERIAL_LIST_ITEM_ID = \"id\";\n    public static final String MATERIAL_LIST_ITEM_COUNT = \"count\";\n    public static final String MATERIAL_LIST_ITEM_NBT = \"nbt\";\n    public static final String MATERIAL_LIST_ITEM_TYPE = \"item_type\";\n    public static final String MATERIAL_LIST_ITEM = \"item\";\n    public static final String MATERIAL_LIST_ITEM_NBT_MATCH = \"nbt_match_type\";\n    public static final String MATERIAL_LIST_CAP_NBT = \"cap_nbt\";\n    public static final String MATERIAL_LIST_CAP_NBT_MATCH = \"cap_nbt_match_type\";\n    public static final String MATERIAL_LIST_ROOT_TYPE = \"root_type\";\n    public static final String MATERIAL_LIST_ROOT_ENTRY = \"root_entry\";\n    public static final String MATERIAL_ENTRIES = \"entries\";\n    public static final String MATERIAL_ENTRY = \"entry\";\n    public static final String MATERIAL_ENTRY_TYPE = \"entry_type\";\n    public static final String HEADER_BOUNDING_BOX = \"bounding_box\";\n    public static final String HEADER_NAME = \"name\";\n    public static final String HEADER_AUTHOR = \"author\";\n    public static final String HEADER_REQUIRED_ITEMS = \"material_list\";\n    public static final String HEADER_VERSION = \"version\";\n    public static final String HEADER_MC_VERSION = \"mc_version\";\n\n    private JsonKeys() {}\n\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/util/ref/NBTKeys.java",
    "content": "package com.direwolf20.buildinggadgets.common.util.ref;\n\nimport net.minecraft.resources.ResourceLocation;\n\nimport static com.direwolf20.buildinggadgets.common.util.ref.Reference.MODID;\n\npublic final class NBTKeys {\n    public static final ResourceLocation AND_SERIALIZER_ID = new ResourceLocation(MODID, \"sub_entries\");\n    public static final String KEY_AUTHOR = \"author\";\n    public static final String KEY_BOUNDS = \"bounds\";\n    public static final String KEY_CAP_COMPARISON = \"cap_comp\";\n    public static final String KEY_CAP_NBT = \"cap_data\";\n    public static final String KEY_COUNT = \"count\";\n    public static final String KEY_DATA = \"data\";\n    public static final String KEY_DATA_COMPARISON = \"data_comp\";\n    public static final String KEY_HEADER = \"header\";\n    public static final String KEY_ID = \"id\";\n    public static final String KEY_MATERIALS = \"materials\";\n    public static final String KEY_MAX_X = \"maxX\";\n    public static final String KEY_MAX_Y = \"maxY\";\n    public static final String KEY_MAX_Z = \"maxZ\";\n    public static final String KEY_MIN_X = \"minX\";\n    public static final String KEY_MIN_Y = \"minY\";\n    public static final String KEY_MIN_Z = \"minZ\";\n    public static final String KEY_NAME = \"name\";\n    public static final String KEY_POS = \"pos\";\n    public static final String KEY_SERIALIZER = \"serializer\";\n    public static final String KEY_STATE = \"state\";\n    public static final String KEY_SUB_ENTRIES = \"sub_entries\";\n    public static final ResourceLocation OR_SERIALIZER_ID = new ResourceLocation(MODID, \"alternatives\");\n    public static final ResourceLocation SIMPLE_SERIALIZER_ID = new ResourceLocation(MODID, \"entries\");\n    private NBTKeys() {}\n\n    public static final String CREATIVE_MARKER = \"creative\";\n\n    public static final String GADGET_MODE = \"mode\";\n    public static final String GADGET_TICKS = \"ticks\";\n    public static final String GADGET_REPLACEMENT_BLOCK = \"replacement_block\";\n    public static final String GADGET_SOURCE_BLOCK = \"source_block\";\n    public static final String GADGET_USE_PASTE = \"use_paste\";\n    public static final String GADGET_OVERLAY = \"overlay\";\n    public static final String GADGET_FUZZY = \"fuzzy\";\n    public static final String GADGET_RAYTRACE_FLUID = \"raytrace_fluid\";\n    public static final String GADGET_PLACE_INSIDE = \"start_inside\";\n    public static final String GADGET_UNCONNECTED_AREA = \"unconnected_area\";\n    public static final String GADGET_ANCHOR = \"anchor\";\n    public static final String GADGET_ANCHOR_SIDE = \"anchor_side\";\n    public static final String GADGET_ANCHOR_COORDS = \"anchor_coords\";\n    public static final String GADGET_UNDO_STACK = \"undo_stack\";\n    public static final String GADGET_UNDO_INT_COORDS = \"undo_int_coords\";\n    public static final String GADGET_UNDO_START_POS = \"undo_start_pos\";\n    public static final String GADGET_REL_POS = \"rel_pos\";\n    public static final String GADGET_UUID = \"uuid\";\n    public static final String GADGET_START_POS = \"start_pos\";\n    public static final String GADGET_END_POS = \"end_pos\";\n    public static final String GADGET_DIM = \"dim\";\n    public static final String GADGET_VALUE_UP = \"up\";\n    public static final String GADGET_VALUE_DOWN = \"down\";\n    public static final String GADGET_VALUE_RIGHT = \"right\";\n    public static final String GADGET_VALUE_LEFT = \"left\";\n    public static final String GADGET_VALUE_DEPTH = \"depth\";\n    public static final String GADGET_FLUID_ONLY = \"fluid\";\n\n    /**\n     * The mapping between an internal block state ID and a block state.\n     */\n    public static final String MAP_STATE = \"state\";\n\n    /**\n     * The mapping between a position index and an internal block state ID.\n     */\n\n    public static final String MAP_SERIALIZE_KEY = \"key\";\n    public static final String MAP_SERIALIZE_VALUE = \"val\";\n\n    public static final String TEMPLATE_COPY_COUNT = \"copy_count\";\n    public static final String TEMPLATE_NAME = \"template_name\";\n    public static final String TEMPLATE_KEY_ID = \"template_id\";\n\n    public static final String PASTE_COUNT = \"amount\";\n\n    public static final String WORD_SAVE_DATA_MAP = \"data_map\";\n    public static final String WORLD_SAVE_TIME = \"time\";\n    public static final String WORLD_SAVE_UNDO_HISTORY = \"undo_history\";\n    public static final String WORLD_SAVE_DIM = \"dim\";\n    public static final String WORLD_SAVE_UNDO_BLOCK_LIST = \"block_list\";\n    public static final String WORLD_SAVE_UNDO_DATA_LIST = \"data_list\";\n    public static final String WORLD_SAVE_UNDO_DATA_SERIALIZER_LIST = \"data_serializer_list\";\n    public static final String WORLD_SAVE_UNDO_ITEMS_LIST = \"items_list\";\n    public static final String WORLD_SAVE_UNDO_ITEMS_SERIALIZER_LIST = \"items_serializer_list\";\n    public static final String WORLD_SAVE_UNDO_RECORDED_DATA = \"recorded_data\";\n    public static final String WORLD_SAVE_UNDO_PLACED_DATA = \"placed_data\";\n    public static final String WORLD_SAVE_UNDO_ITEMS_USED = \"used_items\";\n    public static final String WORLD_SAVE_UNDO_ITEMS_PRODUCED = \"produced_items\";\n    public static final String WORLD_SAVE_UNDO_BOUNDS = \"bounding_box\";\n\n    public static final String REMOTE_INVENTORY_POS = \"bound_te_pos\";\n    public static final String REMOTE_INVENTORY_DIM = \"bound_te_dim\";\n\n    public static final String ENERGY = \"energy\";\n\n    public static final String UNIQUE_ITEM_ITEM = \"item\";\n    public static final String UNIQUE_ITEM_SERIALIZER = \"serializer\";\n    public static final String UNIQUE_ITEM_COUNT = \"count\";\n\n    public static final String TE_CONSTRUCTION_STATE = MAP_STATE;\n    public static final String TE_TEMPLATE_MANAGER_ITEMS = \"items\";\n\n    public static final String ENTITY_DESPAWNING = \"despawning\";\n    public static final String ENTITY_TICKS_EXISTED = \"ticks_existed\";\n    public static final String ENTITY_SET_POS = \"set_pos\";\n\n    public static final String ENTITY_CONSTRUCTION_MAKING_PASTE = \"makingPaste\";\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/util/ref/Reference.java",
    "content": "package com.direwolf20.buildinggadgets.common.util.ref;\n\nimport com.direwolf20.buildinggadgets.common.tainted.Tainted;\nimport net.minecraft.resources.ResourceLocation;\n\n@Tainted(reason = \"Awful. Contains a large amount of unused data or single-query data\")\npublic final class Reference {\n    public static final String MODID = \"buildinggadgets\";\n    public static final String MARKER_BEFORE = MODID + \":before\";\n    public static final ResourceLocation MARKER_BEFORE_RL = new ResourceLocation(MARKER_BEFORE);\n    public static final String MARKER_AFTER = MODID + \":after\";\n    public static final ResourceLocation MARKER_AFTER_RL = new ResourceLocation(MARKER_AFTER);\n\n    public static final ResourceLocation NETWORK_CHANNEL_ID_MAIN = new ResourceLocation(Reference.MODID, \"main_network_channel\");\n    public static final ResourceLocation WORLD_TEMPLATE_PROVIDER_ID = new ResourceLocation(MODID, \"template_provider\");\n\n    private Reference() {\n    }\n\n    public static final class SaveReference {\n        private SaveReference() {\n        }\n\n        public static final String TEMPLATE_SAVE_TEMPLATES = MODID + \"_template_save\";\n        public static final String UNDO_BUILDING = MODID + \"_undo_building\";\n        public static final String UNDO_COPY_PASTE = MODID + \"_undo_copy_paste\";\n        public static final String UNDO_DESTRUCTION = MODID + \"_undo_destruction\";\n        public static final String UNDO_EXCHANGING = MODID + \"_undo_exchanging\";\n    }\n\n    public static final class ItemReference {\n        public static final ResourceLocation TAG_TEMPLATE_CONVERTIBLE = new ResourceLocation(MODID, \"template_convertible\");\n\n        private ItemReference() {\n        }\n    }\n\n    public static final class BlockReference {\n\n        public static final class TagReference {\n            public static final ResourceLocation BLACKLIST_COPY_PASTE = new ResourceLocation(MODID, \"blacklist/copy_paste\");\n            public static final ResourceLocation BLACKLIST_BUILDING = new ResourceLocation(MODID, \"blacklist/building\");\n            public static final ResourceLocation BLACKLIST_EXCHANGING = new ResourceLocation(MODID, \"blacklist/exchanging\");\n            public static final ResourceLocation BLACKLIST_DESTRUCTION = new ResourceLocation(MODID, \"blacklist/destruction\");\n            public static final ResourceLocation WHITELIST_COPY_PASTE = new ResourceLocation(MODID, \"whitelist/copy_paste\");\n            public static final ResourceLocation WHITELIST_BUILDING = new ResourceLocation(MODID, \"whitelist/building\");\n            public static final ResourceLocation WHITELIST_EXCHANGING = new ResourceLocation(MODID, \"whitelist/exchanging\");\n            public static final ResourceLocation WHITELIST_DESTRUCTION = new ResourceLocation(MODID, \"whitelist/destruction\");\n\n            private TagReference() {\n            }\n        }\n\n        private BlockReference() {\n        }\n    }\n\n\n    public static final class TileDataSerializerReference {\n        public static final ResourceLocation REGISTRY_ID_TILE_DATA_SERIALIZER = new ResourceLocation(MODID, \"tile_data/serializer\");\n\n        private TileDataSerializerReference() {\n        }\n    }\n\n    public static final class TileDataFactoryReference {\n        public static final String IMC_METHOD_TILEDATA_FACTORY = \"imc_tile_data_factory\";\n\n        private TileDataFactoryReference() {\n        }\n\n        public static final String DATA_PROVIDER_FACTORY = MODID + \":data_provider_factory\";\n        public static final ResourceLocation DATA_PROVIDER_FACTORY_RL = new ResourceLocation(DATA_PROVIDER_FACTORY);\n    }\n\n    public static final class HandleProviderReference {\n        public static final String IMC_METHOD_HANDLE_PROVIDER = \"imc_handle_provider\";\n\n        private HandleProviderReference() {\n        }\n\n        public static final String STACK_HANDLER_ITEM_HANDLE = MODID + \":stack_handler_provider\";\n        public static final ResourceLocation STACK_HANDLER_ITEM_HANDLE_RL = new ResourceLocation(STACK_HANDLER_ITEM_HANDLE);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/util/spliterator/DelegatingSpliterator.java",
    "content": "package com.direwolf20.buildinggadgets.common.util.spliterator;\n\nimport java.util.Objects;\nimport java.util.Spliterator;\nimport java.util.function.Consumer;\n\n//internal do not use, see package-info\npublic abstract class DelegatingSpliterator<T, U> implements Spliterator<U> {\n    private final Spliterator<T> other;\n    private boolean found;\n\n    protected DelegatingSpliterator(Spliterator<T> other) {\n        this.other = Objects.requireNonNull(other);\n        found = true;\n    }\n\n    @Override\n    public boolean tryAdvance(Consumer<? super U> action) {\n        found = false;\n        while (getOther().tryAdvance(t -> { found = advance(t, action);}) && ! found)\n            ;\n        return found;\n    }\n\n    @Override\n    public int characteristics() {\n        return getOther().characteristics() & (~ SORTED);\n    }\n\n    @Override\n    public long estimateSize() {\n        return getOther().estimateSize();\n    }\n\n    @Override\n    public long getExactSizeIfKnown() {\n        return getOther().getExactSizeIfKnown();\n    }\n\n    protected abstract boolean advance(T object, Consumer<? super U> action);\n\n    protected Spliterator<T> getOther() {\n        return other;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/util/spliterator/MappingSpliterator.java",
    "content": "package com.direwolf20.buildinggadgets.common.util.spliterator;\n\nimport javax.annotation.Nullable;\nimport java.util.Spliterator;\nimport java.util.function.Consumer;\nimport java.util.function.Function;\n\npublic final class MappingSpliterator<T, U> extends DelegatingSpliterator<T, U> {\n    private final Function<? super T, ? extends U> mapper;\n\n    public MappingSpliterator(Spliterator<T> other, Function<? super T, ? extends U> mapper) {\n        super(other);\n        this.mapper = mapper;\n    }\n\n    @Override\n    public boolean advance(T object, Consumer<? super U> action) {\n        action.accept(mapper.apply(object));\n        return true;\n    }\n\n    @Override\n    @Nullable\n    public Spliterator<U> trySplit() {\n        Spliterator<T> split = getOther().trySplit();\n        if (split != null)\n            return new MappingSpliterator<>(split, mapper);\n        return null;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/util/tools/JsonBiDiSerializer.java",
    "content": "package com.direwolf20.buildinggadgets.common.util.tools;\n\nimport com.direwolf20.buildinggadgets.common.tainted.Tainted;\nimport com.google.gson.JsonDeserializer;\nimport com.google.gson.JsonSerializer;\n\n@Tainted(reason = \"Part of the Template system\")\npublic interface JsonBiDiSerializer<T> extends JsonSerializer<T>, JsonDeserializer<T> {\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/util/tools/MathUtils.java",
    "content": "package com.direwolf20.buildinggadgets.common.util.tools;\n\nimport com.direwolf20.buildinggadgets.common.tainted.Tainted;\nimport net.minecraft.core.Direction.Axis;\nimport net.minecraft.world.level.block.Rotation;\nimport net.minecraft.core.BlockPos;\n\n@Tainted(reason = \"Shouldn't exist.\")\npublic final class MathUtils {\n    public static final int B1_BYTE_MASK = 0xFF;\n    public static final int B2_BYTE_MASK = 0xFF_FF;\n    public static final int B3_BYTE_MASK = 0xFF_FF_FF;\n    public static final long B5_BYTE_MASK = ((long) 0xFF_FF_FF_FF) << 8 | 0xFF;\n    private MathUtils() {}\n\n    public static short additiveInverse(short num) {\n        return (short) - num;\n    }\n\n    /**\n     * Converts the BlockPos to a long. Under the assumption, that it is non-negative and does not exceed [0, 255] for the y Coordinate\n     * and [0, 65536] for x and z‬ Coordinates.\n     * @param pos   BlockPos\n     *\n     * @return BlockPos to a long\n     */\n    public static long posToLong(BlockPos pos) {\n        long res = (long) (pos.getX() & B2_BYTE_MASK) << 24;\n        res |= (pos.getY() & B1_BYTE_MASK) << 16; // y-Positions are in [0,255] inclusive\n        res |= (pos.getZ() & B2_BYTE_MASK);\n        return res;\n    }\n\n    public static BlockPos posFromLong(long serialized) {\n        int x = (int) ((serialized >> 24) & B2_BYTE_MASK);\n        int y = (int) ((serialized >> 16) & B1_BYTE_MASK);\n        int z = (int) (serialized & B2_BYTE_MASK);\n        return new BlockPos(x, y, z);\n    }\n\n    public static long includeStateId(long serialized, int id) {\n        return serialized | ((long) (id & B3_BYTE_MASK) << 40);\n    }\n\n    public static int readStateId(long serialized) {\n        return (int) ((serialized >> 40) & B3_BYTE_MASK);\n    }\n\n    public static long readSerializedPos(long serialized) {\n        return serialized & B5_BYTE_MASK;\n    }\n\n    public static int floorMultiple(int i, int factor) {\n        return i - (i % factor);\n    }\n\n    public static int ceilMultiple(int i, int factor) {\n        return i + (i % factor);\n    }\n\n    public static boolean isEven(int i) {\n        return (i & 1) == 0;\n    }\n\n    public static boolean isOdd(int i) {\n        return i % 2 == 1;\n    }\n\n    private static int addForNonEven(int i, int c) {\n        return isEven(i) ? i : i + c;\n    }\n\n    private static int addForNonOdd(int i, int c) {\n        return isOdd(i) ? i : i + c;\n    }\n\n    public static int floorToEven(int i) {\n        return addForNonEven(i, -1);\n    }\n\n    public static int floorToOdd(int i) {\n        return addForNonOdd(i, -1);\n    }\n\n    public static int ceilToEven(int i) {\n        return addForNonEven(i, 1);\n    }\n\n    public static int ceilToOdd(int i) {\n        return addForNonOdd(i, 1);\n    }\n\n    private static int sineForRotation(Rotation rot) {\n        switch (rot) {\n            case NONE:\n            case CLOCKWISE_180:\n                return 0;\n            case CLOCKWISE_90:\n                return 1;\n            case COUNTERCLOCKWISE_90:\n                return - 1;\n            default:\n                throw new AssertionError();\n        }\n    }\n\n    private static int cosineForRotation(Rotation rot) {\n        return sineForRotation(rot.getRotated(Rotation.CLOCKWISE_90));\n    }\n\n    public static int[][] rotationMatrixFor(Axis axis, Rotation rotation) {\n        int[][] matrix = new int[3][3]; //remember it's Java => everything initiated to 0\n        switch (axis) {\n            case X: {\n                matrix[0][0] = 1;\n                matrix[1][1] = cosineForRotation(rotation);\n                matrix[1][2] = sineForRotation(rotation);\n                matrix[2][1] = - sineForRotation(rotation);\n                matrix[2][2] = cosineForRotation(rotation);\n                break;\n            }\n            case Y: {\n                matrix[1][1] = 1;\n                matrix[0][0] = cosineForRotation(rotation);\n                matrix[2][0] = sineForRotation(rotation);\n                matrix[0][2] = - sineForRotation(rotation);\n                matrix[2][2] = cosineForRotation(rotation);\n                break;\n            }\n            case Z: {\n                matrix[2][2] = 1;\n                matrix[0][0] = cosineForRotation(rotation);\n                matrix[0][1] = sineForRotation(rotation);\n                matrix[1][0] = - sineForRotation(rotation);\n                matrix[1][1] = cosineForRotation(rotation);\n                break;\n            }\n        }\n        return matrix;\n    }\n\n    public static BlockPos matrixMul(int[][] matrix, BlockPos pos) {\n        int x = pos.getX() * matrix[0][0] + pos.getY() * matrix[0][1] + pos.getZ() * matrix[0][2];\n        int y = pos.getX() * matrix[1][0] + pos.getY() * matrix[1][1] + pos.getZ() * matrix[1][2];\n        int z = pos.getX() * matrix[2][0] + pos.getY() * matrix[2][1] + pos.getZ() * matrix[2][2];\n        return new BlockPos(x, y, z);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/util/tools/NetworkIO.java",
    "content": "package com.direwolf20.buildinggadgets.common.util.tools;\n\nimport com.direwolf20.buildinggadgets.common.tainted.Tainted;\nimport com.google.common.collect.ImmutableList;\nimport net.minecraft.world.item.ItemStack;\nimport net.minecraftforge.items.IItemHandler;\n\nimport javax.annotation.Nonnull;\nimport javax.annotation.Nullable;\nimport java.util.Collection;\nimport java.util.List;\n\n//import com.raoulvdberge.refinedstorage.api.network.INetwork;\n//import com.raoulvdberge.refinedstorage.api.util.Action;\n\n@Tainted(reason = \"Not being used\")\npublic abstract class NetworkIO implements IItemHandler {\n    private final List<ItemStack> stacks;\n\n    protected NetworkIO(Collection<ItemStack> stacks) {\n        this.stacks = ImmutableList.copyOf(stacks);\n    }\n\n    public static enum Operation {\n        EXTRACT, INSERT\n    }\n\n    @Override\n    public int getSlots() {\n        return stacks.size();\n    }\n\n    @Override\n    @Nonnull\n    public ItemStack getStackInSlot(int slot) {\n        return stacks.get(slot);\n    }\n\n    @Nullable\n    public abstract ItemStack insertItemInternal(ItemStack stack, boolean simulate);\n\n    @Override\n    @Nonnull\n    public ItemStack insertItem(int slot, @Nonnull ItemStack stack, boolean simulate) {\n        return getNonNullStack(insertItemInternal(stack, simulate));\n    }\n\n    @Nullable\n    public abstract ItemStack extractItemInternal(int slot, int amount, boolean simulate);\n\n    @Override\n    @Nonnull\n    public ItemStack extractItem(int slot, int amount, boolean simulate) {\n        return getNonNullStack(extractItemInternal(slot, amount, simulate));\n    }\n\n    private ItemStack getNonNullStack(@Nullable ItemStack stack) {\n        return stack == null ? ItemStack.EMPTY : stack;\n    }\n\n    @Override\n    public int getSlotLimit(int slot) {\n        return Integer.MAX_VALUE;\n    }\n\n    /*\n    public static class NetworkRefinedStorageIO extends NetworkIO {\n        private INetwork network;\n\n        public NetworkRefinedStorageIO(INetwork network, Operation operation) {\n            super(operation == Operation.EXTRACT ? network.getItemStorageCache().getList().getStacks() : Collections.singletonList(ItemStack.EMPTY));\n            this.network = network;\n        }\n\n        @Override\n        @Nullable\n        public ItemStack insertItemInternal(ItemStack stack, boolean simulate) {\n            return network.insertItem(stack, stack.getCount(), getAction(simulate));\n        }\n\n        @Override\n        @Nullable\n        public ItemStack extractItemInternal(int slot, int amount, boolean simulate) {\n            return network.extractItem(getStackInSlot(slot), amount, getAction(simulate));\n        }\n\n        private Action getAction(boolean simulate) {\n            return simulate ? Action.SIMULATE : Action.PERFORM;\n        }\n    }*/\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/util/tools/RegistryUtils.java",
    "content": "package com.direwolf20.buildinggadgets.common.util.tools;\n\nimport net.minecraft.resources.ResourceLocation;\nimport net.minecraft.world.item.Item;\nimport net.minecraft.world.level.block.Block;\nimport net.minecraftforge.registries.ForgeRegistries;\nimport net.minecraftforge.registries.ForgeRegistry;\nimport net.minecraftforge.registries.IForgeRegistry;\n\nimport javax.annotation.Nullable;\n\npublic final class RegistryUtils {\n    private RegistryUtils() {\n    }\n\n    public static <T> int getId(IForgeRegistry<T> registry, T value) {\n        return ((ForgeRegistry<T>) registry).getID(value);\n    }\n\n    public static <T> T getById(IForgeRegistry<T> registry, int id) {\n        return ((ForgeRegistry<T>) registry).getValue(id);\n    }\n\n    @Nullable\n    public static <T> T getFromString(IForgeRegistry<T> registry, String resourceLocation) {\n        return registry.getValue(new ResourceLocation(resourceLocation));\n    }\n\n    public static <T> ResourceLocation getIdFromRegistry(IForgeRegistry<T> registry, T element) {\n        return registry.getKey(element);\n    }\n\n    public static ResourceLocation getItemId(Item item) {\n        return getIdFromRegistry(ForgeRegistries.ITEMS, item);\n    }\n\n    public static ResourceLocation getBlockId(Block item) {\n        return getIdFromRegistry(ForgeRegistries.BLOCKS, item);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/util/tools/SimulateEnergyStorage.java",
    "content": "package com.direwolf20.buildinggadgets.common.util.tools;\n\nimport com.direwolf20.buildinggadgets.common.capability.IPrivateEnergy;\nimport com.direwolf20.buildinggadgets.common.tainted.Tainted;\nimport net.minecraftforge.energy.IEnergyStorage;\n\n/**\n * An {@link IEnergyStorage} which delegates through to another implementation, but only ever performs simulate actions on the backing storage.\n * To provide similar behaviour to regular extractions/insertions to the backing implementation this uses an accumulated buffer and always simulates\n * insertion/extraction of the accumulated buffer.\n * <p>\n * Notice that reported behaviour will be different from the behaviour of the underlying storage (if no extractions are performed), if and only if there\n * are insert/extract limits on the backing storage. In that case this implementation will only work as long as the accumulated buffer is smaller then\n * the defined limits.\n */\n\n@Tainted(reason = \"So stupid. The cap supports simulating by default. This is pointless overhead\")\npublic final class SimulateEnergyStorage implements IPrivateEnergy {\n    private final IEnergyStorage other;\n    private int energyChanged;\n\n    public SimulateEnergyStorage(IEnergyStorage other) {\n        this.other = other;\n    }\n\n    @Override\n    public int receiveEnergy(int maxReceive, boolean simulate) {\n        int received = other.receiveEnergy(maxReceive + energyChanged, true);\n        int dif = Math.max(received - energyChanged, 0);\n        if (! simulate)\n            energyChanged += dif;\n        return dif;\n    }\n\n    @Override\n    public int extractEnergy(int maxExtract, boolean simulate) {\n        return 0;\n    }\n\n    @Override\n    public int extractPower(int maxExtract, boolean simulate) {\n        int extracted = other.extractEnergy(maxExtract - energyChanged, true);\n        int dif = Math.max(extracted + energyChanged, 0);\n        if (! simulate)\n            energyChanged -= dif;\n        return dif;\n    }\n\n    @Override\n    public int getEnergyStored() {\n        return Math.max(other.getEnergyStored() + energyChanged, 0);\n    }\n\n    @Override\n    public int getMaxEnergyStored() {\n        return other.getMaxEnergyStored();\n    }\n\n    @Override\n    public boolean canExtract() {\n        return other.canExtract();\n    }\n\n    @Override\n    public boolean canReceive() {\n        return other.canReceive();\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/world/MockBuilderWorld.java",
    "content": "package com.direwolf20.buildinggadgets.common.world;\n\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.level.block.Blocks;\nimport net.minecraft.world.level.material.FluidState;\nimport net.minecraft.world.level.block.entity.BlockEntity;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.world.level.BlockGetter;\nimport net.minecraft.world.level.Level;\n\nimport javax.annotation.Nullable;\nimport java.util.Collection;\nimport java.util.HashSet;\nimport java.util.Set;\n\npublic class MockBuilderWorld implements BlockGetter {\n    private Set<BlockPos> positions;\n    private BlockState state;\n    private Level realWorld;\n    private final BlockState AIR = Blocks.AIR.defaultBlockState();\n\n    public void setWorldAndState(Level rWorld, BlockState setBlock, Collection<BlockPos> coordinates) {\n        this.state = setBlock;\n        this.realWorld = rWorld;\n        if (coordinates instanceof Set)\n            positions = (Set<BlockPos>) coordinates;\n        else\n            positions = new HashSet<>(coordinates);\n    }\n\n    @Nullable\n    @Override\n    public BlockEntity getBlockEntity(BlockPos pos) {\n        return null;\n    }\n\n    @Override\n    public BlockState getBlockState(BlockPos pos) {\n        return positions.contains(pos) ? state : AIR;\n    }\n\n    @Override\n    public FluidState getFluidState(BlockPos pos) {\n        return null;\n    }\n\n    @Override\n    public int getHeight() {\n        return realWorld.getHeight();\n    }\n\n    @Override\n    public int getMinBuildHeight() {\n        return realWorld.getMinBuildHeight();\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/world/MockDelegationWorld.java",
    "content": "package com.direwolf20.buildinggadgets.common.world;\n\nimport com.direwolf20.buildinggadgets.common.BuildingGadgets;\nimport com.direwolf20.buildinggadgets.common.tainted.Tainted;\nimport com.google.common.base.Preconditions;\nimport net.minecraft.MethodsReturnNonnullByDefault;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;\nimport net.minecraft.core.Holder;\nimport net.minecraft.core.RegistryAccess;\nimport net.minecraft.core.particles.ParticleOptions;\nimport net.minecraft.server.MinecraftServer;\nimport net.minecraft.sounds.SoundEvent;\nimport net.minecraft.sounds.SoundSource;\nimport net.minecraft.util.RandomSource;\nimport net.minecraft.world.Difficulty;\nimport net.minecraft.world.DifficultyInstance;\nimport net.minecraft.world.entity.Entity;\nimport net.minecraft.world.entity.player.Player;\nimport net.minecraft.world.flag.FeatureFlagSet;\nimport net.minecraft.world.flag.FeatureFlags;\nimport net.minecraft.world.level.LevelAccessor;\nimport net.minecraft.world.level.LightLayer;\nimport net.minecraft.world.level.biome.Biome;\nimport net.minecraft.world.level.biome.BiomeManager;\nimport net.minecraft.world.level.block.Block;\nimport net.minecraft.world.level.block.Blocks;\nimport net.minecraft.world.level.block.EntityBlock;\nimport net.minecraft.world.level.block.entity.BlockEntity;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.level.border.WorldBorder;\nimport net.minecraft.world.level.chunk.ChunkAccess;\nimport net.minecraft.world.level.chunk.ChunkSource;\nimport net.minecraft.world.level.chunk.ChunkStatus;\nimport net.minecraft.world.level.dimension.DimensionType;\nimport net.minecraft.world.level.entity.EntityTypeTest;\nimport net.minecraft.world.level.gameevent.GameEvent;\nimport net.minecraft.world.level.levelgen.Heightmap;\nimport net.minecraft.world.level.levelgen.Heightmap.Types;\nimport net.minecraft.world.level.lighting.LevelLightEngine;\nimport net.minecraft.world.level.material.Fluid;\nimport net.minecraft.world.level.material.FluidState;\nimport net.minecraft.world.level.storage.LevelData;\nimport net.minecraft.world.phys.AABB;\nimport net.minecraft.world.phys.Vec3;\nimport net.minecraft.world.phys.shapes.VoxelShape;\nimport net.minecraft.world.ticks.LevelTickAccess;\nimport net.minecraftforge.api.distmarker.Dist;\nimport net.minecraftforge.api.distmarker.OnlyIn;\n\nimport javax.annotation.Nullable;\nimport java.util.*;\nimport java.util.Map.Entry;\nimport java.util.function.Predicate;\n\n@MethodsReturnNonnullByDefault\npublic class MockDelegationWorld implements LevelAccessor {\n    private final LevelAccessor delegate;\n    private Map<BlockPos, BlockInfo> posToBlock;\n\n    public MockDelegationWorld(LevelAccessor delegate) {\n        this.delegate = Objects.requireNonNull(delegate);\n        posToBlock = new HashMap<>();\n    }\n\n    public Set<Entry<BlockPos, BlockInfo>> entrySet() {\n        return Collections.unmodifiableSet(posToBlock.entrySet());\n    }\n\n    public LevelAccessor getDelegate() {\n        return delegate;\n    }\n\n    @Override\n    public void playSound(@Nullable Player player, BlockPos pos, SoundEvent soundIn, SoundSource category, float volume, float pitch) {\n\n    }\n\n    @Override\n    public void addParticle(ParticleOptions particleData, double x, double y, double z, double xSpeed, double ySpeed, double zSpeed) {\n\n    }\n\n    @Override\n    public void levelEvent(@Nullable Player p_217378_1_, int p_217378_2_, BlockPos p_217378_3_, int p_217378_4_) {\n\n    }\n\n    @Override\n    public void gameEvent(GameEvent p_220404_, Vec3 p_220405_, GameEvent.Context p_220406_) {\n\n    }\n\n    @Override\n    public void gameEvent(@Nullable Entity p_151549_, GameEvent p_151550_, BlockPos p_151551_) {\n\n    }\n\n    @Override\n    public List<Entity> getEntities(@Nullable Entity entity, AABB axisAlignedBB, @Nullable Predicate<? super Entity> predicate) {\n        return new ArrayList<>();\n    }\n\n    @Override\n    public <T extends Entity> List<T> getEntities(EntityTypeTest<Entity, T> p_151464_, AABB p_151465_, Predicate<? super T> p_151466_) {\n        return null;\n    }\n\n    @Override\n    public <T extends Entity> List<T> getEntitiesOfClass(Class<T> p_45979_, AABB p_45980_, Predicate<? super T> p_45981_) {\n        return List.of();\n    }\n\n    @Override\n    public List<? extends Player> players() {\n        return new ArrayList<>();\n    }\n\n    @Nullable\n    @Override\n    public ChunkAccess getChunk(int p_217353_1_, int p_217353_2_, ChunkStatus p_217353_3_, boolean p_217353_4_) {\n        return getChunk(p_217353_1_, p_217353_2_);\n    }\n\n    @Override\n    public BlockPos getHeightmapPos(Types heightmapType, BlockPos pos) {\n        return getDelegate().getHeightmapPos(heightmapType, pos);\n    }\n\n    @Override\n    public RegistryAccess registryAccess() {\n        return null;\n    }\n\n    @Override\n    public FeatureFlagSet enabledFeatures() {\n        return FeatureFlags.DEFAULT_FLAGS;\n    }\n\n    @Override\n    public boolean removeBlock(BlockPos blockPos, boolean b) {\n        return removeOverride(blockPos);\n    }\n\n    @Override\n    public boolean isStateAtPosition(BlockPos p_217375_1_, Predicate<BlockState> p_217375_2_) {\n        return p_217375_2_.test(getBlockState(p_217375_1_));\n    }\n\n    @Override\n    public boolean isFluidAtPosition(BlockPos p_151584_, Predicate<FluidState> p_151585_) {\n        return false;\n    }\n\n    @Override\n    @OnlyIn(Dist.CLIENT)\n    public int getMoonPhase() {\n        return delegate.getMoonPhase();\n    }\n\n    /**\n     * Gets the chunk at the specified location.\n     */\n    @Override\n    public ChunkAccess getChunk(int chunkX, int chunkZ) {\n        return delegate.getChunk(chunkX, chunkZ);\n    }\n\n    @Override\n    public long nextSubTickCount() {\n        return delegate.nextSubTickCount();\n    }\n\n    @Override\n    public LevelTickAccess<Block> getBlockTicks() {\n        return delegate.getBlockTicks();\n    }\n\n    @Override\n    public LevelTickAccess<Fluid> getFluidTicks() {\n        return delegate.getFluidTicks();\n    }\n\n    @Override\n    public LevelData getLevelData() {\n        return delegate.getLevelData();\n    }\n\n    @Override\n    public DifficultyInstance getCurrentDifficultyAt(BlockPos pos) {\n        return delegate.getCurrentDifficultyAt(pos);\n    }\n\n    @Nullable\n    @Override\n    public MinecraftServer getServer() {\n        return this.delegate.getServer();\n    }\n\n    @Override\n    public Difficulty getDifficulty() {\n        return delegate.getDifficulty();\n    }\n\n    /**\n     * gets the world's chunk provider\n     */\n    @Override\n    public ChunkSource getChunkSource() {\n        return delegate.getChunkSource();\n    }\n\n\n    @Override\n    public RandomSource getRandom() {\n        return delegate.getRandom();\n    }\n\n//    @Override\n//    public void updateNeighbors(BlockPos p_230547_1_, Block p_230547_2_) {\n//                /*\n//        left blank as we can't notify Blocks as this isn't a subclass of world and it makes no sense notifying the Blocks in the delegate\n//        as they won't know about this Block...\n//        */\n//    }\n\n    /**\n     * Checks to see if an air block exists at the provided location. Note that this only checks to see if the blocks\n     * material is set to air, meaning it is possible for non-vanilla blocks to still pass this check.\n     */\n    @Override\n    public boolean isEmptyBlock(BlockPos pos) {\n        return getBlockState(pos).isAir();\n    }\n\n    @Override\n    public Holder<Biome> getBiome(BlockPos p_204167_) {\n        return delegate.getBiome(p_204167_);\n    }\n\n    @Override\n    public Holder<Biome> getUncachedNoiseBiome(int p_204159_, int p_204160_, int p_204161_) {\n        return delegate.getUncachedNoiseBiome(p_204159_, p_204160_, p_204161_);\n    }\n\n    @Override\n    public int getHeight(Heightmap.Types heightmapType, int x, int z) {\n        return delegate.getHeight(heightmapType, x, z);\n    }\n\n    @Override\n    public int getSkyDarken() {\n        return delegate.getSkyDarken();\n    }\n\n    @Override\n    public BiomeManager getBiomeManager() {\n        return null;\n    }\n\n// removed 1.16\n//    @Override\n//    public BiomeManager getBiomeManager() {\n//        return null;\n//    }\n//\n\n    @Override\n    public WorldBorder getWorldBorder() {\n        return delegate.getWorldBorder();\n    }\n\n    @Override\n    public boolean isUnobstructed(@Nullable Entity entityIn, VoxelShape shape) {\n        return delegate.isUnobstructed(entityIn, shape);\n    }\n\n    @Override\n    public int getDirectSignal(BlockPos pos, Direction direction) {\n        return delegate.getDirectSignal(pos, direction);\n    }\n\n    @Override\n    public boolean isClientSide() {\n        return delegate.isClientSide();\n    }\n\n    @Override\n    public int getSeaLevel() {\n        return delegate.getSeaLevel();\n    }\n\n    @Override\n    public DimensionType dimensionType() {\n        return delegate.dimensionType();\n    }\n\n    @Override\n    @Nullable\n    public BlockEntity getBlockEntity(BlockPos pos) {\n        if (this.delegate.isOutsideBuildHeight(pos))\n            return null;\n        BlockInfo info = getOverriddenBlock(pos);\n        if (info != null)\n            return info.getEntity(this);\n        return delegate.getBlockEntity(pos);\n    }\n\n    @Override\n    public BlockState getBlockState(BlockPos pos) {\n        if (this.delegate.isOutsideBuildHeight(pos))\n            return Blocks.VOID_AIR.defaultBlockState();\n        BlockState state = getOverriddenState(pos);\n        return state != null ? state : Blocks.AIR.defaultBlockState();\n    }\n\n    @Override\n    public FluidState getFluidState(BlockPos pos) {\n        return getBlockState(pos).getFluidState(); // In the end that's what mc does\n    }\n\n    @Override\n    public int getMaxLightLevel() {\n        return delegate.getMaxLightLevel();\n    }\n\n    @Override\n    public boolean setBlock(BlockPos p_241211_1_, BlockState p_241211_2_, int p_241211_3_, int p_241211_4_) {\n        return false;\n    }\n\n    /**\n     * Sets a block state into this world.Flags are as follows:\n     * 1 will cause a block update.\n     * 2 will send the change to clients.\n     * 4 will prevent the block from being re-rendered.\n     * 8 will force any re-renders to run on the main thread instead\n     * 16 will prevent neighbor reactions (e.g. fences connecting, observers pulsing).\n     * 32 will prevent neighbor reactions from spawning drops.\n     * 64 will signify the block is being moved.\n     * Flags can be OR-ed\n     */\n    @Override\n    public boolean setBlock(BlockPos pos, BlockState newState, int flags) {\n        if (this.delegate.isOutsideBuildHeight(pos))\n            return false;\n        BlockInfo info = getOverriddenBlock(pos);\n        if (info != null) {\n            info.setState(newState);\n        } else\n            posToBlock.put(pos, createInfo(pos, newState));\n        return true;\n    }\n\n    /**\n     * Sets a block to air, but also plays the sound and particles and can spawn drops\n     */\n    @Override\n    public boolean destroyBlock(BlockPos pos, boolean dropBlock) {\n        // adapted from World\n        return !this.getBlockState(pos).isAir() && removeBlock(pos, true);\n    }\n\n    @Override\n    public boolean destroyBlock(BlockPos pos, boolean dropBlock, @Nullable Entity entity, int recursionLeft) {\n        return false;\n    }\n\n    //-------------------Extra Methods--------------------\n\n    @Nullable\n    public BlockInfo getOverriddenBlock(BlockPos pos) {\n        return posToBlock.get(pos);\n    }\n\n    @Nullable\n    public BlockState getOverriddenState(BlockPos pos) {\n        BlockInfo info = getOverriddenBlock(pos);\n        return info != null ? info.getState() : null;\n    }\n\n    @Nullable\n    public BlockEntity getOverriddenTile(BlockPos pos) {\n        BlockInfo info = getOverriddenBlock(pos);\n        return info != null ? info.getEntity(this) : null;\n    }\n\n    public void clear() {\n        posToBlock.clear();\n    }\n\n    public boolean removeOverride(BlockPos pos) {\n        BlockInfo info = posToBlock.remove(pos);\n        if (info != null) {\n            info.onRemove();\n            return true;\n        }\n        return false;\n    }\n\n    protected BlockInfo createInfo(BlockPos pos, BlockState state) {\n        return new BlockInfo(pos, state);\n    }\n\n    @Override\n    public int getBrightness(LightLayer p_45518_, BlockPos p_45519_) {\n        return delegate.getBrightness(p_45518_, p_45519_);\n    }\n\n    @Override\n    public float getShade(Direction p_230487_1_, boolean p_230487_2_) {\n        return 0;\n    }\n\n    @Override\n    public LevelLightEngine getLightEngine() {\n        return delegate.getLightEngine();\n    }\n\n    @Tainted(reason = \"Pointless system, also, uncommented...\")\n    public static class BlockInfo {\n        private BlockPos pos;\n        private BlockState state;\n        @Nullable\n        private BlockEntity entity;\n\n        public BlockInfo(BlockPos pos, BlockState state) {\n            this.pos = Objects.requireNonNull(pos);\n            this.state = Objects.requireNonNull(state);\n        }\n\n        public BlockPos getPos() {\n            return pos;\n        }\n\n        public BlockInfo setPos(BlockPos pos) {\n            this.pos = Objects.requireNonNull(pos);\n            return this;\n        }\n\n        public BlockState getState() {\n            return state;\n        }\n\n        public BlockInfo setState(BlockState state) {\n            Preconditions.checkNotNull(state);\n            if (this.state.getBlock() != state.getBlock() || !state.hasBlockEntity()) {\n                onRemove();\n            }\n            this.state = state;\n            return this;\n        }\n\n        @Nullable\n        public BlockEntity getEntity(LevelAccessor world) {\n            if (entity == null && state.hasBlockEntity()) {\n                try {\n                    entity = ((EntityBlock) state.getBlock()).newBlockEntity(pos, state);\n                    if (entity != null) {\n\n                        //if we pass our wrapped world down to this, it will cause it to determine an errornous blockstate...\n                        //we'd need to reflect into the te...\n\n                        entity.onLoad();\n                    }\n                } catch (Exception e) {\n                    BuildingGadgets.LOG.debug(\"Tile Entity at {} with state {} threw exception whilst creating.\", pos, state, e);\n                }\n            }\n            return entity;\n        }\n\n        public void onRemove() {\n            if (entity != null) {\n                try {\n                    entity.setRemoved();\n                } catch (Exception e) {\n                    BuildingGadgets.LOG.debug(\"Tile Entity at {} with state {} threw exception whilst removing.\", pos, state, e);\n                }\n                entity = null;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/direwolf20/buildinggadgets/common/world/MockTileEntityRenderWorld.java",
    "content": "package com.direwolf20.buildinggadgets.common.world;\n\nimport com.direwolf20.buildinggadgets.common.tainted.Tainted;\nimport net.minecraft.client.Minecraft;\nimport net.minecraft.client.renderer.blockentity.BlockEntityRenderer;\nimport net.minecraft.core.BlockPos;\nimport net.minecraft.world.level.BlockGetter;\nimport net.minecraft.world.level.block.EntityBlock;\nimport net.minecraft.world.level.block.entity.BlockEntity;\nimport net.minecraft.world.level.block.state.BlockState;\nimport net.minecraft.world.level.material.FluidState;\n\nimport javax.annotation.Nullable;\nimport java.util.HashMap;\nimport java.util.Map;\n\n@Tainted(reason = \"Shouldn't exist\")\npublic class MockTileEntityRenderWorld implements BlockGetter {\n\n    private final Map<BlockState, BlockEntityRenderer<BlockEntity>> tileEntityRenders = new HashMap<>();\n    private final Map<BlockState, BlockEntity> tileEntities = new HashMap<>();\n\n    public BlockEntityRenderer<BlockEntity> getTileEntityRender(BlockState state) {\n        if (!tileEntityRenders.containsKey(state)) {\n            BlockEntity te = getTileEntity(state);\n            BlockEntityRenderer<BlockEntity> teRender = Minecraft.getInstance().getBlockEntityRenderDispatcher().getRenderer(te);\n            tileEntityRenders.put(state, teRender);\n        }\n\n        return tileEntityRenders.get(state);\n    }\n\n    public BlockEntity getTileEntity(BlockState state) {\n        if (!tileEntities.containsKey(state)) {\n            BlockEntity te = ((EntityBlock) state.getBlock()).newBlockEntity(tileEntities.get(state).getBlockPos(), state);\n            if (te == null) {\n                return tileEntities.get(state);\n            }\n\n            tileEntities.put(state, te);\n        }\n\n        return tileEntities.get(state);\n    }\n\n    @Nullable\n    @Override\n    public BlockEntity getBlockEntity(BlockPos pos) {\n        return null;\n    }\n\n    @Override\n    public BlockState getBlockState(BlockPos pos) {\n        return null;\n    }\n\n    @Override\n    public FluidState getFluidState(BlockPos pos) {\n        return null;\n    }\n\n    @Override\n    public int getHeight() {\n        return 0;\n    }\n\n    @Override\n    public int getMinBuildHeight() {\n        return 0;\n    }\n}\n"
  },
  {
    "path": "src/main/resources/META-INF/accesstransformer.cfg",
    "content": "# All the flowing are in support of the render system being able to render blocks with an alpha\n# START OF RENDER ALPHA SECTION\npublic net.minecraft.client.renderer.RenderType$CompositeState f_110576_ # texture\npublic net.minecraft.client.renderer.RenderStateShard$TextureStateShard f_110328_ # texture\npublic net.minecraft.client.renderer.RenderType$CompositeRenderType f_110511_ # renderState\npublic net.minecraft.client.renderer.RenderType$CompositeRenderType\n# END OF RENDER ALPHA SECTION"
  },
  {
    "path": "src/main/resources/META-INF/mods.toml",
    "content": "modLoader = \"javafml\"\nlicense = \"MIT (https://github.com/Direwolf20-MC/BuildingGadgets/blob/master/LICENSE.md)\"\nloaderVersion = \"[${forgeshortversion},)\"\nupdateJSONURL = \"https://github.com/Direwolf20-MC/BuildingGadgets/raw/master/update.json\"\nissueTrackerURL = \"https://github.com/Direwolf20-MC/BuildingGadgets/issues\"\ndisplayURL = \"https://github.com/Direwolf20-MC/BuildingGadgets\"\nlogoFile = \"buildinggadgets_logo.png\"\ncredits = \"The Forge and FML guys, McJty, Vazkii, CPW, and Pahimar for helping me learn java/forge modding. Rorax for the textures!\"\nauthors = \"Direwolf20, ErrorMikey\"\n\n[[mods]]\nmodId = \"buildinggadgets\"\nversion = \"${version}\"\ndisplayName = \"Building Gadgets\"\ndescription = '''Adds some gadgets to help you build!\n            Building and Exchanging Gadget offer basic building & exchanging functionality for easily creating walls/columns etc.\n            The Destruction Gadget will help you clear out large Areas for your complex builds which you can Copy and Paste at will with the Copy-Paste Gadget!\n            '''\n\n[[dependencies.buildinggadgets]]\nmodId=\"forge\"\nmandatory=true\nversionRange=\"[${forgeversion},)\"\nordering=\"NONE\"\nside=\"BOTH\"\n"
  },
  {
    "path": "src/main/resources/assets/buildinggadgets/blockstates/blank_const_block.json",
    "content": "{\n  \"forge_marker\": 1,\n  \"variants\": {\n    \"\": {\n      \"model\": \"buildinggadgets:block/construction_block\"\n\t}\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/buildinggadgets/blockstates/construction_block.json",
    "content": "{\n  \"forge_marker\": 1,\n  \"variants\": {\n    \"\": {\n      \"model\": \"buildinggadgets:block/construction_block\"\n\t}\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/buildinggadgets/blockstates/construction_block_dense.json",
    "content": "{\n  \"forge_marker\": 1,\n  \"variants\": {\n    \"\": {\n      \"model\": \"buildinggadgets:block/construction_block_dense\"\n\t}\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/buildinggadgets/blockstates/construction_block_powder.json",
    "content": "{\n  \"forge_marker\": 1,\n  \"variants\": {\n    \"\": {\n      \"model\": \"buildinggadgets:block/construction_block_powder\"\n\t}\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/buildinggadgets/blockstates/constructionblock_dense.json",
    "content": "{\n  \"forge_marker\": 1,\n  \"defaults\": {\n    \"model\": \"buildinggadgets:block/constructionblock_dense\"\n  },\n  \"variants\": {\n    \"normal\": [{}],\n    \"inventory\": [{}]\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/buildinggadgets/blockstates/effect_block.json",
    "content": "{\n  \"forge_marker\": 1,\n  \"variants\": {\n    \"\": {\n      \"model\": \"buildinggadgets:block/effect_block\"\n\t}\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/buildinggadgets/blockstates/template_manager.json",
    "content": "{\n  \"forge_marker\": 1,\n  \"defaults\": {\n    \"model\": \"buildinggadgets:block/template_manager\"\n  },\n  \"variants\": {\n    \"facing=north\": {\n      \"model\": \"buildinggadgets:block/template_manager\"\n    },\n    \"facing=south\": {\n      \"y\": 180,\n      \"model\": \"buildinggadgets:block/template_manager\"\n    },\n    \"facing=west\": {\n      \"y\": 270,\n      \"model\": \"buildinggadgets:block/template_manager\"\n    },\n    \"facing=east\": {\n      \"y\": 90,\n      \"model\": \"buildinggadgets:block/template_manager\"\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/buildinggadgets/lang/de_de.json",
    "content": "{\n  \"_comment\": \"itemGroup:\",\n  \"itemGroup.buildingGadgets\": \"Building Gadgets\",\n\n  \"_comment\": \"singles:\",\n  \"singles.buildinggadgets.depth\": \"Tiefe\",\n  \"singles.buildinggadgets.down\": \"Unten\",\n  \"singles.buildinggadgets.left\": \"Links\",\n  \"singles.buildinggadgets.right\": \"Rechts\",\n  \"singles.buildinggadgets.up\": \"Oben\",\n  \"singles.buildinggadgets.confirm\": \"Ok\",\n  \"singles.buildinggadgets.cancel\": \"Abbrechen\",\n\n  \"_comment\": \"item:\",\n  \"item.buildinggadgets.buildingtool\": \"Bau-Gadget\",\n  \"item.buildinggadgets.constructionpaste\": \"Baupaste\",\n  \"item.buildinggadgets.constructionpastecontainer\": \"Pastenbehälter\",\n  \"item.buildinggadgets.constructionpastecontainert2\": \"Pastenbehälter T2\",\n  \"item.buildinggadgets.constructionpastecontainert3\": \"Pastenbehälter T3\",\n  \"item.buildinggadgets.construction_paste_container_creative\": \"Kreativer Pastenbehälter\",\n  \"item.buildinggadgets.copypastetool\": \"Kopier-und-Einfüge-Gadget\",\n  \"item.buildinggadgets.destructiontool\": \"Demolier-Gadget\",\n  \"item.buildinggadgets.exchangertool\": \"Austausch-Gadget\",\n  \"item.buildinggadgets.template\": \"Schablone\",\n\n  \"_comment\": \"block:\",\n  \"block.buildinggadgets.constructionblock\": \"Baustoffblock\",\n  \"block.buildinggadgets.constructionblockpowder\": \"Baustoffpulver\",\n  \"block.buildinggadgets.effectblock\": \"(Unbenutzt)Effektblock\",\n  \"block.buildinggadgets.templatemanager\": \"Schablonen-Manager\",\n\n  \"_comment\": \"key:\",\n  \"key.anchorKey\": \"Festsetzen\",\n  \"key.categories.buildingGadgets\": \"Building Gadgets\",\n  \"key.modeSwitch\": \"Modus ändern\",\n  \"key.rangeChange\": \"Reichweite\",\n  \"key.undo\": \"Rückgängig\",\n  \"key.buildinggadgets.fuzzy\": \"Ungenauer Modus\",\n  \"key.buildinggadgets.connected_area\": \"Zusammenhängender Bereich\",\n\n  \"_comment\": \"tooltip:\",\n  \"tooltip.constructionblockpowder.helptext\": \"Neben Wasser zu platzieren\",\n  \"tooltip.gadget.block\": \"Block\",\n  \"tooltip.gadget.destroyshowoverlay\": \"Overlay angezeigt\",\n  \"tooltip.gadget.destroywarning\": \"WARNUNG: Dieses Gadget zerstört Blöcke.\",\n  \"tooltip.gadget.energy\": \"Energie\",\n  \"tooltip.gadget.mode\": \"Modus\",\n  \"tooltip.gadget.range\": \"Reichweite\",\n  \"tooltip.gadget.raytrace_fluid\": \"Raytrace Flüssigkeiten\",\n  \"tooltip.gadget.building.place_atop\": \"Platziere oben auf\",\n  \"tooltip.gadget.connected\": \"Verbunden\",\n  \"tooltip.gadget.connectedarea\": \"Zusammenhängender Bereich\",\n  \"tooltip.pasteContainer.amount\": \"Menge\",\n  \"tooltip.pasteContainer.creative.amount\": \"Unendlich!!\",\n  \"tooltip.template.name\": \"Name\",\n\n  \"_comment\": \"message:\",\n  \"message.gadget.anchorremove\": \"Festsetzen aufgehoben\",\n  \"message.gadget.anchorrender\": \"Darstellung festgesetzt\",\n  \"message.gadget.areareset\": \"Kopierbereich zurückgesetzt\",\n  \"message.gadget.copied\": \"Bereich kopiert\",\n  \"message.gadget.copyfailed\": \"Kopieren fehlgeschlagen - Keine verwendbaren Daten vorhanden\",\n  \"message.gadget.copyguierror\": \"Bereich konnte nicht kopiert werden\",\n  \"message.gadget.copysuccess\": \"Erfolgreich in die Zwischenablage kopiert\",\n  \"message.gadget.destroysizeerror\": \"Maximaler Demolierungsbereich ist 16x16x16, negative Zahlen sind nicht erlaubt\",\n  \"message.gadget.fuzzymode\": \"Ungenauerer Modus\",\n  \"message.gadget.raytrace_fluid\": \"Raytrace Flüssigkeiten\",\n  \"message.gadget.building.placement\": \"Blöcke platzieren: %s\",\n  \"message.gadget.building.placement.atop\": \"Auf\",\n  \"message.gadget.building.placement.inside\": \"Innen\",\n  \"message.gadget.connectedarea\": \"Verbundener Bereich\",\n  \"message.gadget.connectedsurface\": \"Verbundene Oberfläche\",\n  \"message.gadget.invalidblock\": \"Block ungültig\",\n  \"message.gadget.nothingtoundo\": \"Nichts zum Rückgängigmachen\",\n  \"message.gadget.pastefailed\": \"Einfügen fehlgeschlagen, ungültiges JSON\",\n  \"message.gadget.pastefailed.linkcopied\": \"Einfügen fehlgeschlagen. Bitte kopiere die tatsächlichen JSON-Daten und nicht den Link.\",\n  \"message.gadget.pastesuccess\": \"Einfügen aus Zwischenablage erfolgreich\",\n  \"message.gadget.pastetoobig\": \"Einzufügender Datensatz ist zu groß\",\n  \"message.gadget.rotated\": \"Blöcke rotiert\",\n  \"message.gadget.mirrored\": \"Blöcke gespiegelt\",\n  \"message.gadget.toobigarea\": \"Bereich zu groß, maximaler Bereich ist 125x125x125 (Rechtsklick in die Luft zum Zurücksetzen)\",\n  \"message.gadget.toolmode\": \"Werkzeugmodus\",\n  \"message.gadget.toolrange\": \"Werkzeugreichweite\",\n  \"message.gadget.toomanyblocks\": \"Zu viele Blöcke, Limit ist 32768 (Rechtsklick in die Luft zum Zurücksetzen)\",\n  \"message.gadget.undofailed\": \"Fehler beim Rückgängigmachen (zu weit weg?)\",\n  \"message.gadget.TEinCopy\": \"Bereich Kopiert, Anzahl an nicht kopierten Tile's\",\n  \"message.gadget.boundTE\": \"Gadget and Inventar gebunden\",\n  \"message.gadget.unboundTE\": \"Gadget vom Inventar gelöst\",\n\n  \"_comment\": \"help:\",\n  \"help.gui.buildinggadgets.area.arrow.data_flow\": \"Stellt den Datenfluss von einem Item auf ein anderes dar, entweder von Schablone zu Gadget oder einer anderen Schablone, oder von Gadget/Schablone auf eine Schablone oder ein Stück Papier.\",\n  \"help.gui.buildinggadgets.area.field.template_name\": \"Wenn Daten gespeichert oder eingefügt werden, wird die erzeugte Schablone diesen Namen haben.\",\n  \"help.gui.buildinggadgets.area.preview\": \"Vorschau der im Gadget oder einer Schablone gespeicherten Daten\",\n  \"help.gui.buildinggadgets.area.slot.gadget\": \"§nKopier-und-Einfüge-Gadgets§r oder §nSchablonen§r können von hier aus eingelesen oder beschrieben werden.\\\\n\\\\nEin §9farbiges Overlay§r zeigt an, wenn das Item Daten erhalten wird.\",\n  \"help.gui.buildinggadgets.area.slot.template\": \"§nPapier§r oder §nSchablonen§r können von hier aus eingelesen oder beschrieben werden\\\\n\\\\nEin §9farbiges Overlay§r zeigt an, wenn das Item Daten erhalten wird.\",\n  \"help.gui.buildinggadgets.button.copy\": \"Kopiert Daten von Gadget oder Schablone in die Zwischenablage deines Computers.\",\n  \"help.gui.buildinggadgets.button.help.enter\": \"Hilfstext anzeigen\",\n  \"help.gui.buildinggadgets.button.help.exit\": \"Hilfsmodus beenden\",\n  \"help.gui.buildinggadgets.button.load\": \"Lädt Daten von einer Schablone auf ein Gadget oder eine andere Schablone\",\n  \"help.gui.buildinggadgets.button.paste\": \"Speichert Daten in der Zwischenablage deines Computers in eine andere Schablone oder ein Stück Papier (In diesem Fall wird eine Schablone kreiert).\",\n  \"help.gui.buildinggadgets.button.save\": \"Speichert Daten im Gadget oder einer Schablone auf eine andere Schablone oder ein Stück Papier (In diesem Fall wird eine Schablone kreiert).\",\n  \"_comment\": \"config\",\n  \"config.buildinggadgets.general.rayTraceRange\": \"Maximale Reichweite\",\n  \"config.buildinggadgets.general.rayTraceRange.tooltip\": \"Spezifiziert wie weit entfernt du bauen kannst\",\n  \"config.buildinggadgets.general.poweredByFE\": \"Verwendet FE-Energie\",\n  \"config.buildinggadgets.general.poweredByFE.tooltip\": \"Setzte auf 'true' für FE-Support, setze auf 'false' für Vanilla Item Damage\",\n  \"config.buildinggadgets.general.enablePaste\": \"Aktiviere Kopierpaste\",\n  \"config.buildinggadgets.general.enablePaste.tooltip\": \"Setze auf 'false' um die Kopierpaste zu deaktivieren\",\n  \"config.buildinggadgets.general.enableDestructionGadget\": \"Aktiviere Demolier-Gadget\",\n  \"config.buildinggadgets.general.enableDestructionGadget.tooltip\": \"Setze auf 'false' um das Demolier-Gadget zu deaktivieren\",\n  \"config.buildinggadgets.general.absoluteCoordDefault\": \"Verwende absolute Koordinaten als Standard\",\n  \"config.buildinggadgets.general.absoluteCoordDefault.tooltip\": \"Bestimmt ob die Koordinaten des Kopier/Einfüge-Gadgets standardmäßig im absoluten Modus startet.\\\\n Setze auf 'true' für absoluten Modus, setzte auf 'false' für relativen.\",\n  \"config.buildinggadgets.general.canOverwriteBlocks\": \"Erlaube überschreiben von nicht-Luft-Blöcken\",\n  \"config.buildinggadgets.general.canOverwriteBlocks.tooltip\": \"Entscheidet, ob das Bau/Kopier/Einfüge-Gadget Blöcke wie Wasser, Lava, Gras, etc (Wie z.B. ein Spieler) überschreiben kann.\\\\n 'false' erlaubt nur das ersetzten von Luft-Blöcken.\",\n  \"config.buildinggadgets.general.subCategoryBlacklist\": \"Blacklist Einstellungen\",\n  \"config.buildinggadgets.general.subCategoryBlacklist.tooltip\": \"Konfiguriere deine Blacklist-Einstellungen hier\",\n  \"config.buildinggadgets.general.subCategoryBlacklist.blockBlacklist\": \"Blacklisted Blocks\",\n  \"config.buildinggadgets.general.subCategoryBlacklist.blockBlacklist.tooltip\": \"All Blöcke die hier hinzugefügt werden, werden behandelt wie TileEntities. Überhaupt nicht. \\\\nWohlgemerkt kannst du \\\"Regular Expressions\\\" wie sie von Java Patterns beschrieben werden verwenden um komplexere Namenskombinationen auszudrücken. \\\\nVerwende zum Beispiel \\\"awfulmod:.*\\\" um alle awfulmod Blöcke von der Verwendung auszuschließen.\",\n  \"config.buildinggadgets.general.subCategoryGadgets\": \"Gadgets\",\n  \"config.buildinggadgets.general.subCategoryGadgets.tooltip\": \"Konfiguriere die Gadgets hier\",\n  \"config.buildinggadgets.general.subCategoryGadgets.maxRange\": \"Maximale Reichweite\",\n  \"config.buildinggadgets.general.subCategoryGadgets.maxRange.tooltip\": \"Die maximale Reichweite der Gadget's\",\n  \"config.buildinggadgets.general.subCategoryGadgets.maxEnergy\": \"Maximale Energy\",\n  \"config.buildinggadgets.general.subCategoryGadgets.maxEnergy.tooltip\": \"Die maximale Energie von Bau, Austausch & Kopier/Einfüge Gadget\",\n  \"config.buildinggadgets.general.subCategoryGadgets.energyCost\": \"Energie Kosten\",\n  \"config.buildinggadgets.general.subCategoryGadgets.energyCost.tooltip\": \"Die Energie Kosten des Gadget's pro Operation\",\n  \"config.buildinggadgets.general.subCategoryGadgets.damageCost\": \"Schaden's Kosten\",\n  \"config.buildinggadgets.general.subCategoryGadgets.damageCost.tooltip\": \"Die Schaden's Kosten des Gadget's pro Operation\",\n  \"config.buildinggadgets.general.subCategoryGadgets.durability\": \"Haltbarkeit\",\n  \"config.buildinggadgets.general.subCategoryGadgets.durability.tooltip\": \"Die Haltbarkeit des Gadget's (0 bedeutet, keine Haltbarkeit verwendet) (Ignoriert falls durch FE-Energie betrieben)\",\n  \"config.buildinggadgets.general.subCategoryGadgets.gadgetBuilding\": \"Bau Gadget\",\n  \"config.buildinggadgets.general.subCategoryGadgets.gadgetBuilding.tooltip\": \"Energie Kosten & Haltbarkeit des Bau-Gadget's\",\n  \"config.buildinggadgets.general.subCategoryGadgets.gadgetExchanger\": \"Exchanging Gadget\",\n  \"config.buildinggadgets.general.subCategoryGadgets.gadgetExchanger.tooltip\": \"Energie Kosten & Haltbarkeit des Austausch-Gadget's\",\n  \"config.buildinggadgets.general.subCategoryGadgets.gadgetDestruction\": \"Destruction Gadget\",\n  \"config.buildinggadgets.general.subCategoryGadgets.gadgetDestruction.tooltip\": \"Energie Kosten, Haltbarkeit & Maximale Energie des Demolier-Gadget's\",\n  \"config.buildinggadgets.general.subCategoryGadgets.gadgetDestruction.maxEnergy\": \"Maximale Energie\",\n  \"config.buildinggadgets.general.subCategoryGadgets.gadgetDestruction.maxEnergy.tooltip\": \"Die maximale Energie des Demolier-Gadget's\",\n  \"config.buildinggadgets.general.subCategoryGadgets.gadgetDestruction.nonfuzzy.multiplier\": \"Genauigkeits Multiplikator\",\n  \"config.buildinggadgets.general.subCategoryGadgets.gadgetDestruction.nonfuzzy.multiplier.tooltip\": \"Die Energie/Haltbarkeit's Kosten des Demolier-Gadget's werden mit diesem Wert multiplieziert, sofern es sich nicht im ungenauen Modus befindet\",\n  \"config.buildinggadgets.general.subCategoryGadgets.gadgetDestruction.nonfuzzy.enabled\": \"Genauer Modus Verfügbar\",\n  \"config.buildinggadgets.general.subCategoryGadgets.gadgetDestruction.nonfuzzy.enabled.tooltip\": \"Falls aktiviert, kann das Demolier-Gadget den ungenauen Modus verlassen und für erhöhte Kosten nur noch den angeklickten Block zuerstören.\",\n  \"config.buildinggadgets.general.subCategoryGadgets.gadgetCopyPaste\": \"Kopier/Einfüge Gadget\",\n  \"config.buildinggadgets.general.subCategoryGadgets.gadgetCopyPaste.tooltip\": \"Energie Kosten & Haltbarkeit des Kopier/Einfüge-Gadget's\",\n  \"config.buildinggadgets.general.subCategoryPasteContainers\": \"Pastenbehälter\",\n  \"config.buildinggadgets.general.subCategoryPasteContainers.tooltip\": \"Konfiguriere die Pastenbehälter hier\",\n  \"config.buildinggadgets.general.subCategoryPasteContainers.capacity.t1\": \"T1 Pastenbehälter-Kapazität\",\n  \"config.buildinggadgets.general.subCategoryPasteContainers.capacity.t1.tooltip\": \"Die maximale Kapazität eines tier 1 (Eisen) Pastenbehälter\",\n  \"config.buildinggadgets.general.subCategoryPasteContainers.capacity.t2\": \"T2 Pastenbehälter-Kapazität\",\n  \"config.buildinggadgets.general.subCategoryPasteContainers.capacity.t2.tooltip\": \"Die maximale Kapazität eines tier 2 (Gold) Pastenbehälter\",\n  \"config.buildinggadgets.general.subCategoryPasteContainers.capacity.t3\": \"T3 Pastenbehälter-Kapazität\",\n  \"config.buildinggadgets.general.subCategoryPasteContainers.capacity.t3.tooltip\": \"Die maximale Kapazität eines tier 3 (Diamanto) Pastenbehälter\"\n}"
  },
  {
    "path": "src/main/resources/assets/buildinggadgets/lang/en_us.json",
    "content": "{\n  \"_comment\": \"itemGroup:\",\n  \"itemGroup.buildinggadgets\": \"Building Gadgets\",\n  \"_comment\": \"item:\",\n  \"item.buildinggadgets.gadget_building\": \"Building Gadget\",\n  \"item.buildinggadgets.gadget_copy_paste\": \"Copy-Paste Gadget\",\n  \"item.buildinggadgets.gadget_destruction\": \"Destruction Gadget\",\n  \"item.buildinggadgets.gadget_exchanging\": \"Exchanging Gadget\",\n  \"item.buildinggadgets.construction_paste\": \"Construction Paste\",\n  \"item.buildinggadgets.construction_chunk_dense\": \"Dense Construction Chunk\",\n  \"item.buildinggadgets.construction_paste_container_t1\": \"Paste Container\",\n  \"item.buildinggadgets.construction_paste_container_t2\": \"Paste Container T2\",\n  \"item.buildinggadgets.construction_paste_container_t3\": \"Paste Container T3\",\n  \"item.buildinggadgets.construction_paste_container_creative\": \"Creative Paste Container\",\n  \"item.buildinggadgets.template\": \"Template\",\n  \"_comment\": \"block:\",\n  \"block.buildinggadgets.construction_block\": \"Construction Block\",\n  \"block.buildinggadgets.construction_block_dense\": \"Dense Construction Block\",\n  \"block.buildinggadgets.construction_block_powder\": \"Construction Block Powder\",\n  \"block.buildinggadgets.effect_block\": \"(Unused)Effect Block\",\n  \"block.buildinggadgets.template_manager\": \"Template Manager\",\n  \"block.buildinggadgets.charging_station\": \"Charging Station\",\n  \"_comment\": \"key:\",\n  \"key.buildinggadgets.category\": \"Building Gadgets\",\n  \"key.buildinggadgets.anchor\": \"Anchor\",\n  \"key.buildinggadgets.settings_menu\": \"Settings Menu\",\n  \"key.buildinggadgets.range\": \"Range / Overlay\",\n  \"key.buildinggadgets.rotate_mirror\": \"Rotate / Mirror\",\n  \"key.buildinggadgets.undo\": \"Undo\",\n  \"key.buildinggadgets.fuzzy\": \"Fuzzy\",\n  \"key.buildinggadgets.connected_area\": \"Connected Area\",\n  \"key.buildinggadgets.material_list\": \"Open Material List\",\n  \"_comment\": \"tooltip:\",\n  \"tooltip.constructionblockpowder.helptext\": \"Place next to water\",\n  \"tooltip.gadget.block\": \"Block: %s\",\n  \"tooltip.gadget.destroyshowoverlay\": \"Showing Overlay: %s\",\n  \"tooltip.gadget.destroywarning\": \"WARNING: This tool voids blocks.\",\n  \"tooltip.gadget.energy\": \"Energy: %s/%s\",\n  \"tooltip.gadget.mode\": \"Mode: %s\",\n  \"tooltip.gadget.rotate\": \"Rotate\",\n  \"tooltip.gadget.mirror\": \"Mirror\",\n  \"tooltip.gadget.range\": \"Range: %s (%s blocks)\",\n  \"tooltip.gadget.anchor\": \"Anchor\",\n  \"tooltip.gadget.undo\": \"Undo\",\n  \"tooltip.gadget.fuzzy\": \"Fuzzy: %s\",\n  \"tooltip.gadget.raytrace_fluid\": \"Raytrace Fluid: %s\",\n  \"tooltip.gadget.building.place_atop\": \"Place On Top: %s\",\n  \"tooltip.gadget.connected\": \"Connected %s\",\n  \"tooltip.gadget.connected_area\": \"Connected Area: %s\",\n  \"tooltip.gadget.connected_surface\": \"Connected Surface\",\n  \"tooltip.pasteContainer.amount\": \"Amount: %d / %d\",\n  \"tooltip.pasteContainer.creative.amountMsg\": \"Amount: Infinite!\",\n  \"tooltip.charger.energy\": \"Energy: %s FE\",\n  \"tooltip.charger.burn_time\": \"Burn time left: %ss\",\n  \"tooltip.charger.fuel_empty\": \"No fuel\",\n  \"tooltip.template.name\": \"Name: %s\",\n  \"tooltip.template.author\": \"Author: %s\",\n  \"tooltip.donotuse\": \"DO NOT USE (WIP)\",\n\n  \"_comment\": \"commands\",\n  \"buildinggadgets.commands.force_unloaded.no_player\": \"Cannot toggle player status without a player unload status to target!\",\n  \"buildinggadgets.commands.force_unloaded.toggled\": \"Set ForceLoadedChunks for player %s to %b.\",\n  \"buildinggadgets.commands.force_unloaded.list\": \"Player with UUID %s has ForceLoadedChunks set to %b.\",\n  \"buildinggadgets.commands.override_copy_size.no_player\": \"Cannot toggle player status without a player copy status to target!\",\n  \"buildinggadgets.commands.override_copy_size.toggled\": \"Set OverrideCopySize for player %s to %b.\",\n  \"buildinggadgets.commands.override_copy_size.list\": \"Player with UUID %s has OverrideCopySize set to %b.\",\n  \"buildinggadgets.commands.override_build_size.no_player\": \"Cannot toggle player status without a player build status to target!\",\n  \"buildinggadgets.commands.override_build_size.toggled\": \"Set OverrideBuildSize for player %s to %b.\",\n  \"buildinggadgets.commands.override_build_size.list\": \"Player with UUID %s has OverrideBuildSize set to %b.\",\n\n  \"_comment\": \"radial-menu\",\n  \"buildinggadgets.radialmenu.destruction_overlay\": \"Show Overlay\",\n  \"buildinggadgets.radialmenu.fluid_only\": \"Fluid Only Mode\",\n  \"buildinggadgets.radialmenu.rotate\": \"Rotate\",\n  \"buildinggadgets.radialmenu.mirror\": \"Mirror\",\n  \"buildinggadgets.radialmenu.fuzzy\": \"Fuzzy\",\n  \"buildinggadgets.radialmenu.connected_area\": \"Connected Area\",\n  \"buildinggadgets.radialmenu.connected_surface\": \"Connected Surface\",\n  \"buildinggadgets.radialmenu.open_gui\": \"Open Gui\",\n  \"buildinggadgets.radialmenu.open_material-list\": \"Open Material List\",\n  \"buildinggadgets.radialmenu.raytrace_fluid\": \"Raytrace Fluid\",\n  \"buildinggadgets.radialmenu.place_on_top\": \"Place on Top\",\n  \"buildinggadgets.radialmenu.anchor\": \"Anchor\",\n  \"buildinggadgets.radialmenu.undo\": \"Undo\",\n  \"_comment\": \"modes:\",\n  \"buildinggadgets.modes.surface\": \"Surface\",\n  \"buildinggadgets.modes.grid\": \"Grid\",\n  \"buildinggadgets.modes.horizontal_column\": \"Horizontal Column\",\n  \"buildinggadgets.modes.horizontal_wall\": \"Horizontal Wall\",\n  \"buildinggadgets.modes.vertical_column\": \"Vertical Column\",\n  \"buildinggadgets.modes.vertical_wall\": \"Vertical Wall\",\n  \"buildinggadgets.modes.stairs\": \"Stairs\",\n  \"buildinggadgets.modes.build_to_me\": \"Build To Me\",\n  \"buildinggadgets.modes.copy\": \"Copy\",\n  \"buildinggadgets.modes.paste\": \"Paste\",\n  \"_comment\": \"message:\",\n  \"buildinggadgets.message.copy_unloaded\": \"Attempted to perform a copy which would have enclosed %d unloaded chunks. If you want to copy nonetheless, please use the ForceLoadChunks command.\",\n  \"buildinggadgets.message.undo_unloaded\": \"Attempted to perform an undo which would have enclosed %d unloaded chunks. If you want to undo nonetheless, please use the ForceLoadChunks command.\",\n  \"buildinggadgets.message.build_unloaded\": \"Attempted to perform a build which would have enclosed %d unloaded chunks. If you want to build nonetheless, please use the ForceLoadChunks command.\",\n  \"buildinggadgets.message.copy_too_large\": \"Attempted to perform a copy of size (%d, %d, %d) which is larger then your server's defined max size of (%d, %d, %d). If you want to copy nonetheless, please use the OverrideCopySize command.\",\n  \"buildinggadgets.message.build_too_large\": \"Attempted to perform a build of size (%d, %d, %d) which is larger then your server's defined max size of (%d, %d, %d). If you want to build nonetheless, please use the OverrideBuildSize command.\",\n  \"buildinggadgets.message.server_busy\": \"The Server is currently Busy. Please Wait.\",\n  \"buildinggadgets.message.gadget_busy\": \"The Gadget is currently Busy. Please Wait.\",\n  \"buildinggadgets.message.area_too_big\": \"Area too Large, max area is 65 536 X 256 X 65 536 (Right click empty air to reset)\",\n  \"buildinggadgets.message.too_many_blocks\": \"Too Many Blocks, Limit is 2 147 483 647 (Right click empty air to reset)\",\n  \"buildinggadgets.message.too_many_dif_blocks\": \"Too Many different Blocks, Limit is 16 777 216 (Right click empty air to reset)\",\n  \"buildinggadgets.message.template_build\": \"Template build\",\n  \"buildinggadgets.message.copied\": \"Area Copied\",\n  \"buildinggadgets.message.not_copied\": \"Area Copy Failed\",\n  \"buildinggadgets.message.anchor_removed\": \"Anchor Removed\",\n  \"buildinggadgets.message.anchor_set\": \"Render Anchored\",\n  \"buildinggadgets.message.area_reset\": \"Copy Area Reset\",\n  \"buildinggadgets.message.copy_failed.template_write\": \"Could not write Template binary.\",\n  \"buildinggadgets.message.copy_failed.error\": \"Could not copy to clipboard.\",\n  \"buildinggadgets.message.copy_clipboard_success\": \"Copy to Clipboard successful\",\n  \"buildinggadgets.message.destroy_size_too_large\": \"Maximum Destruction size is %1$dx%1$dx%1$d\",\n  \"buildinggadgets.message.fuzzy_mode\": \"Fuzzy Mode: %s\",\n  \"buildinggadgets.message.raytrace_fluid\": \"Raytracing Fluids: %s\",\n  \"buildinggadgets.message.building.placement\": \"Block Placement: %s\",\n  \"buildinggadgets.message.place.atop\": \"On Top\",\n  \"buildinggadgets.message.place.inside\": \"Inside\",\n  \"buildinggadgets.message.connected_area\": \"Connected Area: %s\",\n  \"buildinggadgets.message.connected_surface\": \"Connected Surface: %s\",\n  \"buildinggadgets.message.invalid_block\": \"Invalid Block (%s)\",\n  \"buildinggadgets.message.undo_missing_items\": \"Missing Items for Undo\",\n  \"buildinggadgets.message.undo_failed\": \"Undo Failed (Too far away?)\",\n  \"buildinggadgets.message.nothing_to_undo\": \"Nothing To Undo\",\n  \"buildinggadgets.message.paste_failed\": \"Failed to paste Template to clipboard.\",\n  \"buildinggadgets.message.paste_failed.wrong_mc_version\": \"Paste Failed, MC Version %s is not supported. Please use only templates that were build for (%s) -> (%s)\",\n  \"buildinggadgets.message.paste_failed.too_recent_version\": \"Paste Failed, Template version %s is unknown to this instance. Please use only Templates that were build up to format version %s.\",\n  \"buildinggadgets.message.paste_failed.invalid_json\": \"Paste Failed, Invalid JSON\",\n  \"buildinggadgets.message.paste_failed.corrupt_json\": \"Paste Failed, Corrupt JSON\",\n  \"buildinggadgets.message.paste_failed.corrupt_body\": \"Paste Failed, Template body is corrupted!\",\n  \"buildinggadgets.message.paste_failed.link_copied\": \"Paste Failed. Please copy the raw json data, instead of a Link.\",\n  \"buildinggadgets.message.paste_success\": \"Paste from Clipboard successful\",\n  \"buildinggadgets.message.rotated\": \"Blocks Rotated\",\n  \"buildinggadgets.message.mirrored\": \"Blocks Mirrored\",\n  \"buildinggadgets.message.tool_mode\": \"Tool Mode: %s\",\n  \"buildinggadgets.message.range_set\": \"Tool Range: %d\",\n  \"buildinggadgets.message.boundTE\": \"Bound Gadget to Inventory\",\n  \"buildinggadgets.message.unboundTE\": \"Unbound Gadget from Inventory\",\n  \"buildinggadgets.message.failed_to_bind\": \"Failed to bind to inventory\",\n  \"buildinggadgets.message.invalid_inventory\": \"This block is not a valid inventory\",\n  \"buildinggadgets.message.first_copy\": \"First Position set\",\n  \"buildinggadgets.easter_eggs.9x9.for_the_collection\": \"Another 9 by 9 for Dire'S collection... Go submit it!\",\n  \"buildinggadgets.easter_eggs.9x9.just_like_dire\": \"A 9 by 9 just like Dire's.\",\n  \"buildinggadgets.easter_eggs.9x9.dire_would_be_proud\": \"Dire would be proud.\",\n  \"buildinggadgets.easter_eggs.9x9.no_dire_wire\": \"No Dire-Wire please.\",\n  \"buildinggadgets.easter_eggs.dire_9x9.another_one\": \"Another one... Really Dire?!?\",\n  \"buildinggadgets.easter_eggs.dire_9x9.dont_tell\": \"Don't tell your viewers.\",\n  \"buildinggadgets.easter_eggs.dire_9x9.correct_size\": \"You sure that's the correct size?\",\n  \"buildinggadgets.easter_eggs.dire_9x9.no_dire_wire\": \"No Dire-Wire this time. Please.\",\n\n  \"_comment\": \"gui - template manager\",\n  \"gui.buildinggadgets.tm.button.copy\": \"Copy\",\n  \"gui.buildinggadgets.tm.button.paste\": \"Paste\",\n  \"gui.buildinggadgets.tm.button.save\": \"Save\",\n  \"gui.buildinggadgets.tm.button.load\": \"Load\",\n  \"gui.buildinggadgets.tm.name_field.text\": \"name?\",\n  \"gui.buildinggadgets.tm.field.placeholder\": \"Template name\",\n  \"_comment\": \"gui - single words\",\n  \"gui.buildinggadgets.single.confirm\": \"Confirm\",\n  \"gui.buildinggadgets.single.cancel\": \"Cancel\",\n  \"gui.buildinggadgets.single.close\": \"Close\",\n  \"gui.buildinggadgets.single.clear\": \"Clear\",\n  \"gui.buildinggadgets.single.reset\": \"Reset\",\n  \"gui.buildinggadgets.single.range\": \"Range\",\n  \"gui.buildinggadgets.field.start\": \"Start\",\n  \"gui.buildinggadgets.field.end\": \"End\",\n  \"_comment\": \"gui - destruction:\",\n  \"gui.buildinggadgets.copy.button.absolute\": \"Use absolute coords\",\n  \"gui.buildinggadgets.copy.label.heading\": \"Make adjustments\",\n  \"gui.buildinggadgets.copy.label.subheading\": \"Use absolute mode to use block locations.\",\n  \"gui.buildinggadgets.destruction.field.depth\": \"Depth\",\n  \"gui.buildinggadgets.destruction.field.down\": \"Down\",\n  \"gui.buildinggadgets.destruction.field.left\": \"Left\",\n  \"gui.buildinggadgets.destruction.field.right\": \"Right\",\n  \"gui.buildinggadgets.destruction.field.up\": \"Up\",\n  \"_comment\": \"gui - material list:\",\n  \"gui.buildinggadgets.materialList.button.close\": \"Close\",\n  \"gui.buildinggadgets.materialList.button.copyList\": \"Copy\",\n  \"gui.buildinggadgets.materialList.button.sorting.nameAZ\": \"A-Z\",\n  \"gui.buildinggadgets.materialList.button.sorting.nameZA\": \"Z-A\",\n  \"gui.buildinggadgets.materialList.button.sorting.requiredAcse\": \"Required ↓\",\n  \"gui.buildinggadgets.materialList.button.sorting.requiredDesc\": \"Required ↑\",\n  \"gui.buildinggadgets.materialList.button.sorting.missingAcse\": \"Missing ↓\",\n  \"gui.buildinggadgets.materialList.button.sorting.missingDesc\": \"Missing ↑\",\n  \"gui.buildinggadgets.materialList.message.copyList.success\": \"Successfully copied material list\",\n  \"gui.buildinggadgets.materialList.titleEmpty\": \"Material List\",\n  \"gui.buildinggadgets.materialList.titleNameOnly\": \"Material List for %s\",\n  \"gui.buildinggadgets.materialList.titleAuthorOnly\": \"Material List by %s\",\n  \"gui.buildinggadgets.materialList.title\": \"Material List for %s by %s\",\n  \"gui.buildinggadgets.materialList.help.copyList\": \"Copy a text version of the material list.\\nPress Ctrl/Command to create a detailed version with code names\",\n  \"_comment\": \"gui - help:\",\n  \"gui.buildinggadgets.help.area.arrow.data_flow\": \"Represents the flow of data from one item to another; either from a template to a gadget/template, or from a gadget/template to a template or piece of paper\",\n  \"gui.buildinggadgets.help.area.field.template_name\": \"When data is saved/pasted, the resulting template will have this name\",\n  \"gui.buildinggadgets.help.area.preview\": \"Preview of the data stored in the gadget/template\",\n  \"gui.buildinggadgets.help.area.slot.gadget\": \"Accepts §ncopy-paste gadget§r or §ntemplate§r to be saved/copied from and loaded to \\\\n\\\\n§9Colored overlay§r indicates that the item will receive data\",\n  \"gui.buildinggadgets.help.area.slot.template\": \"Accepts §npaper§r or §ntemplate§r to be loaded from and saved/pasted to\\\\n\\\\n§9Colored overlay§r indicates that the item will receive data\",\n  \"gui.buildinggadgets.help.button.copy\": \"Copies data from a gadget/template to your computer's clipboard\",\n  \"gui.buildinggadgets.help.button.help.enter\": \"Show hovering help text\",\n  \"gui.buildinggadgets.help.button.help.exit\": \"Exit help mode\",\n  \"gui.buildinggadgets.help.button.load\": \"Loads data onto a gadget/template from a template\",\n  \"gui.buildinggadgets.help.button.paste\": \"Pastes data on your computer's clipboard to a pre-existing template, or to a piece of paper, creating a template\",\n  \"gui.buildinggadgets.help.button.save\": \"Saves the data in the gadget/template to a pre-existing template, or to a piece of paper, creating a template\"\n}\n"
  },
  {
    "path": "src/main/resources/assets/buildinggadgets/lang/es_es.json",
    "content": "{\n  \"item.buildinggadgets.buildingtool\": \"Herramienta de construcción\",\n  \"item.buildinggadgets.exchangertool\": \"Herramienta de intercambio\",\n  \"block.buildinggadgets.effectblock\": \"(Sin uso)Bloque de efecto\",\n  \"key.categories.buildingGadgets\": \"Building Gadgets\",\n  \"key.anchorKey\": \"Anclaje\",\n  \"key.modeSwitch\": \"Cambiar de modo\",\n  \"key.rangeChange\": \"Rango\",\n  \"key.undoKey\": \"Deshacer\",\n  \"tooltip.gadget.block\": \"Bloque\",\n  \"tooltip.gadget.mode\": \"Modo\",\n  \"tooltip.gadget.range\": \"Rango\",\n  \"message.gadget.invalidblock\": \"Bloque invalido\",\n  \"message.gadget.toolmode\": \"Modo\",\n  \"message.gadget.toolrange\": \"Rango\",\n  \"message.gadget.anchorrender\": \"Mostrar anclaje\",\n  \"message.gadget.anchorremove\": \"Anclaje eliminado\",\n  \"message.gadget.nothingtoundo\": \"Nada que deshacer\",\n  \"message.gadget.undofailed\": \"Fallo al deshacer (¿Demasiado lejos?)\"\n}"
  },
  {
    "path": "src/main/resources/assets/buildinggadgets/lang/fr_fr.json",
    "content": "{\n    \"_comment\": \"itemGroup:\",\n    \"itemGroup.buildinggadgets\": \"Building Gadgets\",\n\n    \"_comment\": \"item:\",\n    \"item.buildinggadgets.gadget_building\": \"Outil de Construction\",\n    \"item.buildinggadgets.gadget_copy_paste\": \"Outil de Copier-Coller\",\n    \"item.buildinggadgets.gadget_destruction\": \"Outil de Destruction\",\n    \"item.buildinggadgets.gadget_exchanging\": \"Outil d'Échange\",\n    \"item.buildinggadgets.construction_paste\": \"Mortier de Construction\",\n    \"item.buildinggadgets.construction_chunk_dense\": \"Éclat de Mortier Dense\",\n    \"item.buildinggadgets.construction_paste_container_t1\": \"Contenant à Mortier\",\n    \"item.buildinggadgets.construction_paste_container_t2\": \"Contenant à Mortier T2\",\n    \"item.buildinggadgets.construction_paste_container_t3\": \"Contenant à Mortier T3\",\n    \"item.buildinggadgets.construction_paste_container_creative\": \"Contenant à Mortier Créatif\",\n    \"item.buildinggadgets.template\": \"Modèle\",\n\n    \"_comment\": \"block:\",\n    \"block.buildinggadgets.construction_block\": \"Bloc de Mortier\",\n    \"block.buildinggadgets.construction_block_dense\": \"Bloc de Mortier Dense\",\n    \"block.buildinggadgets.construction_block_powder\": \"Bloc de Poudre de Mortier\",\n    \"block.buildinggadgets.effect_block\": \"(Inutilisé)Bloc d'effet\",\n    \"block.buildinggadgets.template_manager\": \"Gestionnaire de Modèles\",\n\n    \"_comment\": \"key:\",\n    \"key.buildinggadgets.category\": \"Building Gadgets\",\n    \"key.buildinggadgets.anchor\": \"Ancrage\",\n    \"key.buildinggadgets.mode_switch\": \"Changer le Mode\",\n    \"key.buildinggadgets.range_change\": \"Portée / Rotation / Superposition\",\n    \"key.buildinggadgets.undo\": \"Annuler\",\n    \"key.buildinggadgets.fuzzy\": \"Flou\",\n    \"key.buildinggadgets.connected_area\": \"Surface Contiguë\",\n\n    \"_comment\": \"tooltip:\",\n    \"tooltip.constructionblockpowder.helptext\": \"Placez près de l'eau\",\n    \"tooltip.gadget.block\": \"Bloc\",\n    \"tooltip.gadget.destroyshowoverlay\": \"Superposition affichée\",\n    \"tooltip.gadget.destroy.overlay\": \"Superposition\",\n    \"tooltip.gadget.destroywarning\": \"AVERTISSEMENT: Cet outils ANNULE les blocs.\",\n    \"tooltip.gadget.energy\": \"Énergie\",\n    \"tooltip.gadget.mode\": \"Mode\",\n    \"tooltip.gadget.range\": \"Portée\",\n    \"tooltip.gadget.fuzzy\": \"Flou\",\n    \"tooltip.gadget.raytrace_fluid\": \"Fluide de rayon de tracé\",\n    \"tooltip.gadget.building.place_atop\": \"Placer sur le dessus\",\n    \"tooltip.gadget.connected\": \"Connecté\",\n    \"tooltip.gadget.connectedarea\": \"Surface Contiguë\",\n    \"tooltip.pasteContainer.amount\": \"Quantité\",\n    \"tooltip.pasteContainer.creative.amountMsg\": \"Quantité: Infinie!\",\n    \"tooltip.template.name\": \"Nom\",\n\n    \"_comment\": \"message:\",\n    \"message.gadget.anchorremove\": \"Ancrage Supprimé\",\n    \"message.gadget.anchorrender\": \"Rendu Ancré\",\n    \"message.gadget.areareset\": \"Surface à Copier Réinitialisée\",\n    \"message.gadget.copied\": \"Surface Copiée\",\n    \"message.gadget.copyfailed\": \"Copie Échouée, aucune informations sur l'outil\",\n    \"message.gadget.copyguierror\": \"Surface à Copier Échouée\",\n    \"message.gadget.copysuccess\": \"Copie au Presse-Papier réussie\",\n    \"message.gadget.destroysizeerror\": \"Taille maximum de destruction est 16x16x16, les nombres négatifs sont invalides\",\n    \"message.gadget.fuzzymode\": \"Mode Flou\",\n    \"message.gadget.raytrace_fluid\": \"Fluide de Rayon de Tracé\",\n    \"message.gadget.building.placement\": \"Placement de Bloc: %s\",\n    \"message.gadget.building.placement.atop\": \"Sur le Dessus\",\n    \"message.gadget.building.placement.inside\": \"À l'Intérieur\",\n    \"message.gadget.connectedarea\": \"Zone Contiguë\",\n    \"message.gadget.connectedsurface\": \"Surface Contiguë\",\n    \"message.gadget.invalidblock\": \"Bloc Invalide\",\n    \"message.gadget.nothingtoundo\": \"Rien à Annuler\",\n    \"message.gadget.pastefailed\": \"Coller Échoué, JSON Invalide\",\n    \"message.gadget.pastefailed.linkcopied\": \"Coller Échouer. SVP copier l'information JSON brute plutôt qu'un lien.\",\n    \"message.gadget.pastesuccess\": \"Coller à partir du Presse-Papier réussi\",\n    \"message.gadget.pastetoobig\": \"L'information à Coller est trop volumineuse\",\n    \"message.gadget.rotated\": \"Blocs Retournées\",\n    \"message.gadget.toobigarea\": \"Aire trop volumineuse, l'aire maximum est 125x125x125 (Clic-droit dans le vide pour réinitialiser)\",\n    \"message.gadget.toolmode\": \"Mode de l'outil\",\n    \"message.gadget.toolrange\": \"Portée de l'outil\",\n    \"message.gadget.toomanyblocks\": \"Trop de blocs, La limite est de 32,768 (Clic-droit dans le vide pour réinitialiser)\",\n    \"message.gadget.undofailed\": \"Échec de l'annulation (Trop loin?)\",\n    \"message.gadget.TEinCopy\": \"Aire copiée, Nombre d'entitées non-copiée\",\n    \"message.gadget.boundTE\": \"Outil synchronisé à l'inventaire\",\n    \"message.gadget.unboundTE\": \"Outil désynchronisé de l'inventaire\",\n\n    \"_comment\": \"gui - destruction:\",\n    \"gui.buildinggadgets.destruction.field.depth\": \"Profondeur\",\n    \"gui.buildinggadgets.destruction.field.down\": \"Bas\",\n    \"gui.buildinggadgets.destruction.field.left\": \"Gauche\",\n    \"gui.buildinggadgets.destruction.field.right\": \"Droite\",\n    \"gui.buildinggadgets.destruction.field.up\": \"Haut\",\n    \"gui.buildinggadgets.destruction.button.confirm\": \"Ok\",\n    \"gui.buildinggadgets.destruction.button.cancel\": \"Annuler\",\n\n    \"_comment\": \"gui - help:\",\n    \"gui.buildinggadgets.help.area.arrow.data_flow\": \"Représente le flux de l'information, d'un objet à un autre; soit d'un modèle vers un outil/modèle, ou d'un outil/modèle vers un modèle ou une feuille de papier\",\n    \"gui.buildinggadgets.help.area.field.template_name\": \"Lorsque l'information est sauvegardée/collée, le modèle portera ce nom\",\n    \"gui.buildinggadgets.help.area.preview\": \"Aperçu de l'informations stockée dans l'outil/modèle\",\n    \"gui.buildinggadgets.help.area.slot.gadget\": \"Accepte un §nOutil de Copier-Coller§r ou un §nmodèle§r afin de sauvegarder/copier ou pour enregistrer vers d'autres\\\\n\\\\n§9Colored overlay§r indique que l'objet recevra de l'information\",\n    \"gui.buildinggadgets.help.area.slot.template\": \"Accepte du §npapier§r ou un §nmodèle§r afin de sauvegarder/copier ou pour enregistrer vers d'autres \\\\n\\\\n§9Colored overlay§r indique que l'objet recevra de l'information\",\n    \"gui.buildinggadgets.help.button.copy\": \"Copie l'information d'un outil/modèle vers le presse-papier de votre ordinateur\",\n    \"gui.buildinggadgets.help.button.help.enter\": \"Afficher le texte d'aide en survolement\",\n    \"gui.buildinggadgets.help.button.help.exit\": \"Quitter le mode Aide\",\n    \"gui.buildinggadgets.help.button.load\": \"Charge l'information sur un outil/modèle depuis un modèle\",\n    \"gui.buildinggadgets.help.button.paste\": \"Colle l'information du presse-papier vers un modèle existant, ou sur une feuille de papier, créant un nouveau modèle\",\n    \"gui.buildinggadgets.help.button.save\": \"Charge l'information d'un outil/modèle vers un modèle existant, ou sur une feuille de papier, créant un nouveau modèle\"\n}\n"
  },
  {
    "path": "src/main/resources/assets/buildinggadgets/lang/ja_jp.json",
    "content": "{\n  \"item.buildinggadgets.buildingtool\": \"建てのガジェット\",\n  \"item.buildinggadgets.exchangertool\": \"替えのガジェット\",\n  \"block.buildinggadgets.effectblock\": \"（未使用）エフェクトブロク\",\n  \"key.categories.buildingGadgets\": \"建てのガジェットス\",\n  \"key.anchorKey\": \"固定\",\n  \"key.modeSwitch\": \"スイッチモード\",\n  \"key.rangeChange\": \"距離\",\n  \"key.undoKey\": \"アンドウ\",\n  \"tooltip.gadget.block\": \"ブロク\",\n  \"tooltip.gadget.mode\": \"モード\",\n  \"tooltip.gadget.range\": \"距離\",\n  \"message.gadget.invalidblock\": \"ブロクは無効です。\",\n  \"message.gadget.toolmode\": \"モードのガジェット\",\n  \"message.gadget.toolrange\": \"距離のガジェッと\",\n  \"message.gadget.anchorrender\": \"レンダーは固定です。\",\n  \"message.gadget.anchorremove\": \"レンダーは固定じゃないです。\",\n  \"message.gadget.nothingtoundo\": \"元に戻すことはありません。\",\n  \"message.gadget.undofailed\": \"元に戻すことができませんでした。（あまりにも遠いですか？）\"\n}"
  },
  {
    "path": "src/main/resources/assets/buildinggadgets/lang/no_no.json",
    "content": "{\n  \"item.buildinggadgets.buildingtool\": \"Byggeapparat\",\n  \"item.buildinggadgets.exchangertool\": \"Erstatningsapparat\",\n  \"block.buildinggadgets.effectblock\": \"(Ubrukt)Effekt Blokk\",\n  \"key.categories.buildingGadgets\": \"Byggeapparater\",\n  \"key.anchorKey\": \"Forankring\",\n  \"key.modeSwitch\": \"Bytte Modus\",\n  \"key.rangeChange\": \"Rekkevidde\",\n  \"key.undoKey\": \"Angre\",\n  \"tooltip.gadget.block\": \"Blokk\",\n  \"tooltip.gadget.mode\": \"Modus\",\n  \"tooltip.gadget.range\": \"Rekkevidde\",\n  \"message.gadget.invalidblock\": \"Ugyldig Blokk\",\n  \"message.gadget.toolmode\": \"Apparatmodus\",\n  \"message.gadget.toolrange\": \"Apparatrekkevidde\",\n  \"message.gadget.anchorrender\": \"Forankret planen\",\n  \"message.gadget.anchorremove\": \"Forankring fjernet\",\n  \"message.gadget.nothingtoundo\": \"Ingenting å angre\",\n  \"message.gadget.undofailed\": \"Angring mislyktes (For langt unna?)\"\n}"
  },
  {
    "path": "src/main/resources/assets/buildinggadgets/lang/pl_pl.json",
    "content": "{\n  \"item.buildinggadgets.buildingtool\": \"Budujący Gadżet\",\n  \"item.buildinggadgets.exchangertool\": \"Zamieniający Gadżet\",\n  \"block.buildinggadgets.effectblock\": \"(Nieużywane) Blok Efektu\",\n  \"key.categories.buildingGadgets\": \"Gadżety Budowania\",\n  \"key.anchorKey\": \"Zakotwicz\",\n  \"key.modeSwitch\": \"Zmień tryb\",\n  \"key.rangeChange\": \"Zasięg\",\n  \"key.undoKey\": \"Cofnij\",\n  \"tooltip.gadget.block\": \"Blok\",\n  \"tooltip.gadget.mode\": \"Tryb\",\n  \"tooltip.gadget.range\": \"Zasięg\",\n  \"tooltip.gadget.energy\": \"Energia\",\n  \"message.gadget.invalidblock\": \"Niepoprawny blok\",\n  \"message.gadget.toolmode\": \"Tryb narzędzia\",\n  \"message.gadget.toolrange\": \"Zasięg narzędzia\",\n  \"message.gadget.anchorrender\": \"Rysuj zakotwiczenie\",\n  \"message.gadget.anchorremove\": \"Usuń zakotwiczenie\",\n  \"message.gadget.nothingtoundo\": \"Nie ma co cofać\",\n  \"message.gadget.undofailed\": \"Cofnięcie nieudane (Za daleko?)\",\n  \"message.gadget.fuzzymode\": \"Fuzzy Mode\",\n  \"item.buildinggadgets.constructionpastecontainer\": \"Wklejający Pojemnik\",\n  \"item.buildinggadgets.constructionpaste\": \"Konstrukcyjny Pojemnik\",\n  \"tooltip.pasteContainer.amount\": \"Ilość\",\n  \"block.buildinggadgets.constructionblock\": \"Konstrukcyjny Blok\",\n  \"block.buildinggadgets.constructionblockpowder\": \"Konstrukcyjny Proszek\",\n  \"tooltip.constructionblockpowder.helptext\": \"Postaw obok wody\"\n}"
  },
  {
    "path": "src/main/resources/assets/buildinggadgets/lang/pt_br.json",
    "content": "{\n  \"_comment\": \"itemGroup:\",\n  \"itemGroup.buildinggadgets\": \"Gadgets de construção\",\n  \"_comment\": \"item:\",\n  \"item.buildinggadgets.gadget_building\": \"Gadget de construção\",\n  \"item.buildinggadgets.gadget_copy_paste\": \"Gadget de cópia-colas\",\n  \"item.buildinggadgets.gadget_destruction\": \"Gadget de destruição\",\n  \"item.buildinggadgets.gadget_exchanging\": \"Trocando gadgets\",\n  \"item.buildinggadgets.construction_paste\": \"Pasta de construção\",\n  \"item.buildinggadgets.construction_chunk_dense\": \"Pedaço de construção densa\",\n  \"item.buildinggadgets.construction_paste_container_t1\": \"Colar contêiner\",\n  \"item.buildinggadgets.construction_paste_container_t2\": \"Cole o contêiner T2\",\n  \"item.buildinggadgets.construction_paste_container_t3\": \"Colar contêiner T3\",\n  \"item.buildinggadgets.construction_paste_container_creative\": \"Container de pasta criativa\",\n  \"item.buildinggadgets.template\": \"Modelo\",\n  \"_comment\": \"block:\",\n  \"block.buildinggadgets.construction_block\": \"Bloco de construção\",\n  \"block.buildinggadgets.construction_block_dense\": \"Bloco de construção denso\",\n  \"block.buildinggadgets.construction_block_powder\": \"Pó de bloco de construção\",\n  \"block.buildinggadgets.effect_block\": \"Bloco de efeito (não utilizado)\",\n  \"block.buildinggadgets.template_manager\": \"Gerenciador de modelos\",\n  \"block.buildinggadgets.charging_station\": \"Estação para carregar\",\n  \"_comment\": \"key:\",\n  \"key.buildinggadgets.category\": \"Gadgets de construção\",\n  \"key.buildinggadgets.anchor\": \"Âncora\",\n  \"key.buildinggadgets.settings_menu\": \"Menu Configurações\",\n  \"key.buildinggadgets.range\": \"Alcance / sobreposição\",\n  \"key.buildinggadgets.rotate_mirror\": \"Gire / espelho\",\n  \"key.buildinggadgets.undo\": \"Desfazer\",\n  \"key.buildinggadgets.fuzzy\": \"Difusa\",\n  \"key.buildinggadgets.connected_area\": \"Área conectada\",\n  \"key.buildinggadgets.material_list\": \"Lista de materiais abertos\",\n  \"_comment\": \"tooltip:\",\n  \"tooltip.constructionblockpowder.helptext\": \"Coloque ao lado da água\",\n  \"tooltip.gadget.block\": \"Quadra: %s\",\n  \"tooltip.gadget.destroyshowoverlay\": \"Mostrando sobreposição: %s\",\n  \"tooltip.gadget.destroywarning\": \"Aviso: esta ferramenta anula os blocos.\",\n  \"tooltip.gadget.energy\": \"Energia: %s/%s\",\n  \"tooltip.gadget.mode\": \"Modo: %s\",\n  \"tooltip.gadget.rotate\": \"Girar\",\n  \"tooltip.gadget.mirror\": \"Espelho\",\n  \"tooltip.gadget.range\": \"Variar: %s (%sblocoss)\",\n  \"tooltip.gadget.anchor\": \"Âncora\",\n  \"tooltip.gadget.undo\": \"Desfazer\",\n  \"tooltip.gadget.fuzzy\": \"Difusa: %s\",\n  \"tooltip.gadget.raytrace_fluid\": \"raytraceFluid: %s\",\n  \"tooltip.gadget.building.place_atop\": \"Coloque no topo: %s\",\n  \"tooltip.gadget.connected\": \"Conectada %s\",\n  \"tooltip.gadget.connected_area\": \"Área conectada: %s\",\n  \"tooltip.gadget.connected_surface\": \"Superfície conectada\",\n  \"tooltip.pasteContainer.amount\": \"Quantia: %d / %d\",\n  \"tooltip.pasteContainer.creative.amountMsg\": \"Quantidade: Infinito!\",\n  \"tooltip.charger.energy\": \"Energia: %s FE\",\n  \"tooltip.charger.burn_time\": \"Tempo de queima restante: %ss\",\n  \"tooltip.charger.fuel_empty\": \"Não combustível\",\n  \"tooltip.template.name\": \"Nome: %s\",\n  \"tooltip.template.author\": \"Autor(a): %s\",\n  \"tooltip.donotuse\": \"Não use (WIP)\",\n\n  \"_comment\": \"commands\",\n  \"buildinggadgets.commands.force_unloaded.no_player\": \"Não é possível alternar o status do player sem um status de descarga de jogador para segmentar!\",\n  \"buildinggadgets.commands.force_unloaded.toggled\": \"Defina forceloadedchunks para o jogaparar %s to %b.\",\n  \"buildinggadgets.commands.force_unloaded.list\": \"Jogador com uuid %s Forcelunkunks tem sido definido para %b.\",\n  \"buildinggadgets.commands.override_copy_size.no_player\": \"Não é possível alternar o status do player sem um status de cópia do jogador para o destino!\",\n  \"buildinggadgets.commands.override_copy_size.toggled\": \"Defina o excedenteCopysize para o jparaador %s to %b.\",\n  \"buildinggadgets.commands.override_copy_size.list\": \"Jogador com UUID %s tem exagero. %b.\",\n  \"buildinggadgets.commands.override_build_size.no_player\": \"Não é possível alternar o status do jogador sem um status de criação de jogadores para segmentar!\",\n  \"buildinggadgets.commands.override_build_size.toggled\": \"Definir SubstrideBuildSize para o joparador %s to %b.\",\n  \"buildinggadgets.commands.override_build_size.list\": \"Jogador com uuid %s tem substituído o conjunto para %b.\",\n\n  \"_comment\": \"radial-menu\",\n  \"buildinggadgets.radialmenu.destruction_overlay\": \"Mostre sobreposição\",\n  \"buildinggadgets.radialmenu.fluid_only\": \"Modo somente fluido\",\n  \"buildinggadgets.radialmenu.rotate\": \"Girar\",\n  \"buildinggadgets.radialmenu.mirror\": \"Espelho\",\n  \"buildinggadgets.radialmenu.fuzzy\": \"Difusa\",\n  \"buildinggadgets.radialmenu.connected_area\": \"Área conectada\",\n  \"buildinggadgets.radialmenu.connected_surface\": \"Superfície conectada\",\n  \"buildinggadgets.radialmenu.open_gui\": \"GUI aberta\",\n  \"buildinggadgets.radialmenu.open_material-list\": \"Lista de materiais abertos\",\n  \"buildinggadgets.radialmenu.raytrace_fluid\": \"raytraceFluid\",\n  \"buildinggadgets.radialmenu.place_on_top\": \"Coloque por cima\",\n  \"buildinggadgets.radialmenu.anchor\": \"Âncora\",\n  \"buildinggadgets.radialmenu.undo\": \"Desfazer\",\n  \"_comment\": \"modes:\",\n  \"buildinggadgets.modes.surface\": \"Superfície\",\n  \"buildinggadgets.modes.grid\": \"Rede\",\n  \"buildinggadgets.modes.horizontal_column\": \"Coluna horizontal\",\n  \"buildinggadgets.modes.horizontal_wall\": \"Parede horizontal\",\n  \"buildinggadgets.modes.vertical_column\": \"Coluna vertical\",\n  \"buildinggadgets.modes.vertical_wall\": \"Parede vertical\",\n  \"buildinggadgets.modes.stairs\": \"Escadas\",\n  \"buildinggadgets.modes.build_to_me\": \"Construir para mim\",\n  \"buildinggadgets.modes.copy\": \"cópia de\",\n  \"buildinggadgets.modes.paste\": \"Colar\",\n  \"_comment\": \"message:\",\n  \"buildinggadgets.message.copy_unloaded\": \"Tentou realizar uma cópia que teria fechado %d pedaços descarregados.Se você deseja copiar, por favor, use o comando forceloadchunks.\",\n  \"buildinggadgets.message.undo_unloaded\": \"Tentou realizar um desfazer que teria fechado %d pedaços descarregados.Se você deseja desfazer, no entanto, use o comando forceloadchunks.\",\n  \"buildinggadgets.message.build_unloaded\": \"Tentou realizar uma compilação que teria fechado %d pedaços descarregados.Se você deseja construir, mesmo assim, use o comando forceloadchunks.\",\n  \"buildinggadgets.message.copy_too_large\": \"Tentou realizar uma cópia do tamanho (%d, %d, %d) que é maior do que o tamanho máximo definido pelo seu servidor de (%d, %d, %d). Se você deseja copiar, no entanto, use o comando substituto do Subsecopysize.\",\n  \"buildinggadgets.message.build_too_large\": \"Tentou realizar uma construção de tamanho (%d, %d, %d) que é maior do que o tamanho máximo definido pelo seu servidor de (%d, %d, %d). Se você deseja construir, mesmo assim, use o comando substitutobuildSize.\",\n  \"buildinggadgets.message.server_busy\": \"O servidor está atualmente ocupado.Por favor, aguarde.\",\n  \"buildinggadgets.message.gadget_busy\": \"O gadget está atualmente ocupado.Por favor, aguarde.\",\n  \"buildinggadgets.message.area_too_big\": \"Área muito grande, a área máxima é 65 536 X 256 X 65 536 (Clique com o botão direito do mouse em vazio para redefinir)\",\n  \"buildinggadgets.message.too_many_blocks\": \"Muitos blocos, limite é 2 147 483 647 (Clique com o botão direito do mouse em vazio para redefinir)\",\n  \"buildinggadgets.message.too_many_dif_blocks\": \"Muitos blocos diferentes, limite é 16 777 216 (Clique com o botão direito do mouse em vazio para redefinir)\",\n  \"buildinggadgets.message.template_build\": \"Construção de modelos\",\n  \"buildinggadgets.message.copied\": \"Área copiada\",\n  \"buildinggadgets.message.not_copied\": \"A cópia da área falhou\",\n  \"buildinggadgets.message.anchor_removed\": \"Âncora removida\",\n  \"buildinggadgets.message.anchor_set\": \"Renderizar ancorado\",\n  \"buildinggadgets.message.area_reset\": \"Copie a redefinição da área\",\n  \"buildinggadgets.message.copy_failed.template_write\": \"Não foi possível escrever modelo binário.\",\n  \"buildinggadgets.message.copy_failed.error\": \"Não foi possível copiar para a área de transferência.\",\n  \"buildinggadgets.message.copy_clipboard_success\": \"Cópia para a área de transferência bem -sucedida\",\n  \"buildinggadgets.message.destroy_size_too_large\": \"O tamanho máximo de destruição é %1$dx%1$dx%1$d\",\n  \"buildinggadgets.message.fuzzy_mode\": \"Modo difuso: %s\",\n  \"buildinggadgets.message.raytrace_fluid\": \"Fluidos de Raytracing: %s\",\n  \"buildinggadgets.message.building.placement\": \"Posicionamento do bloco: %s\",\n  \"buildinggadgets.message.place.atop\": \"Em cima\",\n  \"buildinggadgets.message.place.inside\": \"Lado de dentro\",\n  \"buildinggadgets.message.connected_area\": \"Área conectada: %s\",\n  \"buildinggadgets.message.connected_surface\": \"Superfície conectada: %s\",\n  \"buildinggadgets.message.invalid_block\": \"Bloco inválido (%s)\",\n  \"buildinggadgets.message.undo_missing_items\": \"Itens ausentes para desfazer\",\n  \"buildinggadgets.message.undo_failed\": \"Desfazer falhou (muito longe?)\",\n  \"buildinggadgets.message.nothing_to_undo\": \"Nada para desfazer\",\n  \"buildinggadgets.message.paste_failed\": \"Falha ao colar o modelo para a área de transferência.\",\n  \"buildinggadgets.message.paste_failed.wrong_mc_version\": \"A pasta falhou, o MC Version %S não é suportado.Por favor, use apenas modelos que foram construídos para (%s) -> (%s)\",\n  \"buildinggadgets.message.paste_failed.too_recent_version\": \"Falha na pasta, a versão do modelo %s é desconhecida nesta instância.Por favor, use apenas modelos que foram acumulados para formatar a versão %s.\",\n  \"buildinggadgets.message.paste_failed.invalid_json\": \"Pasta falhou, JSON inválido\",\n  \"buildinggadgets.message.paste_failed.corrupt_json\": \"Pasta falhou, JSON corrupto\",\n  \"buildinggadgets.message.paste_failed.corrupt_body\": \"Pasta falhou, o corpo do modelo está corrompido!\",\n  \"buildinggadgets.message.paste_failed.link_copied\": \"A pasta falhou.Copie os dados JSON RAW, em vez de um link.\",\n  \"buildinggadgets.message.paste_success\": \"Colar da área de transferência bem -sucedida\",\n  \"buildinggadgets.message.rotated\": \"Blocos girados\",\n  \"buildinggadgets.message.mirrored\": \"Blocos espelhados\",\n  \"buildinggadgets.message.tool_mode\": \"Modo de ferramenta: %s\",\n  \"buildinggadgets.message.range_set\": \"Intervalo de ferramentas: %d\",\n  \"buildinggadgets.message.boundTE\": \"Gadget vinculado ao inventário\",\n  \"buildinggadgets.message.unboundTE\": \"Gadget não ligado do inventário\",\n  \"buildinggadgets.message.failed_to_bind\": \"Falha ao se ligar ao inventário\",\n  \"buildinggadgets.message.invalid_inventory\": \"Este bloco não é um inventário válido\",\n  \"buildinggadgets.message.first_copy\": \"Primeira posição definida\",\n  \"buildinggadgets.easter_eggs.9x9.for_the_collection\": \"Outros 9 por 9 para a coleção de Dire ... Vá enviá -la!\",\n  \"buildinggadgets.easter_eggs.9x9.just_like_dire\": \"A 9 por 9, assim como Dire's.\",\n  \"buildinggadgets.easter_eggs.9x9.dire_would_be_proud\": \"Terrível ficaria orgulhoso.\",\n  \"buildinggadgets.easter_eggs.9x9.no_dire_wire\": \"Sem termos de arame, por favor.\",\n  \"buildinggadgets.easter_eggs.dire_9x9.another_one\": \"Outro ... realmente terrível?!?\",\n  \"buildinggadgets.easter_eggs.dire_9x9.dont_tell\": \"Não diga aos seus espectadores.\",\n  \"buildinggadgets.easter_eggs.dire_9x9.correct_size\": \"Você tem certeza que esse é o tamanho correto?\",\n  \"buildinggadgets.easter_eggs.dire_9x9.no_dire_wire\": \"Desta vez, sem termos terríveis.Por favor.\",\n\n  \"_comment\": \"gui - template manager\",\n  \"gui.buildinggadgets.tm.button.copy\": \"cópia de\",\n  \"gui.buildinggadgets.tm.button.paste\": \"Colar\",\n  \"gui.buildinggadgets.tm.button.save\": \"Salvar\",\n  \"gui.buildinggadgets.tm.button.load\": \"Carregar\",\n  \"gui.buildinggadgets.tm.name_field.text\": \"nome?\",\n  \"gui.buildinggadgets.tm.field.placeholder\": \"Nome do modelo\",\n  \"_comment\": \"gui - single words\",\n  \"gui.buildinggadgets.single.confirm\": \"confirme\",\n  \"gui.buildinggadgets.single.cancel\": \"Cancelar\",\n  \"gui.buildinggadgets.single.close\": \"Perto\",\n  \"gui.buildinggadgets.single.clear\": \"Clara\",\n  \"gui.buildinggadgets.single.reset\": \"Redefinir\",\n  \"gui.buildinggadgets.single.range\": \"Variar\",\n  \"gui.buildinggadgets.field.start\": \"Começar\",\n  \"gui.buildinggadgets.field.end\": \"Fim\",\n  \"_comment\": \"gui - destruction:\",\n  \"gui.buildinggadgets.copy.button.absolute\": \"Use coordes absolutos\",\n  \"gui.buildinggadgets.copy.label.heading\": \"Faça ajustes\",\n  \"gui.buildinggadgets.copy.label.subheading\": \"Use o modo absoluto para usar locais de bloco.\",\n  \"gui.buildinggadgets.destruction.field.depth\": \"Profundidade\",\n  \"gui.buildinggadgets.destruction.field.down\": \"Baixa\",\n  \"gui.buildinggadgets.destruction.field.left\": \"Esquerda\",\n  \"gui.buildinggadgets.destruction.field.right\": \"Certa\",\n  \"gui.buildinggadgets.destruction.field.up\": \"Acima\",\n  \"_comment\": \"gui - material list:\",\n  \"gui.buildinggadgets.materialList.button.close\": \"Perto\",\n  \"gui.buildinggadgets.materialList.button.copyList\": \"cópia de\",\n  \"gui.buildinggadgets.materialList.button.sorting.nameAZ\": \"A-Z\",\n  \"gui.buildinggadgets.materialList.button.sorting.nameZA\": \"Z-A\",\n  \"gui.buildinggadgets.materialList.button.sorting.requiredAcse\": \"Requeridas ↓\",\n  \"gui.buildinggadgets.materialList.button.sorting.requiredDesc\": \"Requeridas ↑\",\n  \"gui.buildinggadgets.materialList.button.sorting.missingAcse\": \"Ausência de ↓\",\n  \"gui.buildinggadgets.materialList.button.sorting.missingDesc\": \"Ausência de ↑\",\n  \"gui.buildinggadgets.materialList.message.copyList.success\": \"Lista de materiais copiados com sucesso\",\n  \"gui.buildinggadgets.materialList.titleEmpty\": \"Lista de materiais\",\n  \"gui.buildinggadgets.materialList.titleNameOnly\": \"Lista de materiais para %s\",\n  \"gui.buildinggadgets.materialList.titleAuthorOnly\": \"Lista de materiais por %s\",\n  \"gui.buildinggadgets.materialList.title\": \"Lista de materiais papor %s by %s\",\n  \"gui.buildinggadgets.materialList.help.copyList\": \"Copie uma versão de texto da lista de materiais.\\nPressione Ctrl/comando para criar uma versão detalhada com nomes de código\",\n  \"_comment\": \"gui - help:\",\n  \"gui.buildinggadgets.help.area.arrow.data_flow\": \"Representa o fluxo de dados de um item para outro;de um modelo para um gadget/modelo ou de um gadget/modelo para um modelo ou pedaço de papel\",\n  \"gui.buildinggadgets.help.area.field.template_name\": \"Quando os dados são salvos/colados, o modelo resultante terá esse nome\",\n  \"gui.buildinggadgets.help.area.preview\": \"Visualização dos dados armazenados no gadget/modelo\",\n  \"gui.buildinggadgets.help.area.slot.gadget\": \"Aceita §nGadget de cópia-colas§r ou §nmodelo§r ser salvo/copiado e carregado para \\\\n\\\\n§9Sobreposição colorida§r indica que o item receberá dados\",\n  \"gui.buildinggadgets.help.area.slot.template\": \"Aceita §npapel§r ou §nmodelo§r ser carregado e salvo/colado para\\\\n\\\\n§9Sobreposição colorida§r indica que o item receberá dados\",\n  \"gui.buildinggadgets.help.button.copy\": \"Copia dados de um gadget/modelo para a área de transferência do seu computador\",\n  \"gui.buildinggadgets.help.button.help.enter\": \"Mostrar texto em pairando\",\n  \"gui.buildinggadgets.help.button.help.exit\": \"Saia do modo de ajuda\",\n  \"gui.buildinggadgets.help.button.load\": \"Carrega dados em um gadget/modelo de um modelo\",\n  \"gui.buildinggadgets.help.button.paste\": \"Pasta dados na área de transferência do seu computador em um modelo pré-existente ou em um pedaço de papel, criando um modelo\",\n  \"gui.buildinggadgets.help.button.save\": \"Salva os dados no gadget/modelo em um modelo pré-existente ou em um pedaço de papel, criando um modelo\"\n}\n"
  },
  {
    "path": "src/main/resources/assets/buildinggadgets/lang/ru_RU.json",
    "content": "{\n  \"item.buildinggadgets.buildingtool\": \"Строительный гаджет\",\n  \"item.buildinggadgets.exchangertool\": \"Обменный гаджет\",\n  \"block.buildinggadgets.effectblock\": \"(неиспользующийся)Блок эффекта\",\n  \"key.categories.buildingGadgets\": \"Строительные гаджеты\",\n  \"key.anchorKey\": \"Привязка\",\n  \"key.modeSwitch\": \"Смена режима\",\n  \"key.rangeChange\": \"Диапозон\",\n  \"key.undoKey\": \"Отмена/Все подряд\",\n  \"tooltip.gadget.block\": \"Блок\",\n  \"tooltip.gadget.mode\": \"Режим\",\n  \"tooltip.gadget.range\": \"Диапозон\",\n  \"tooltip.gadget.energy\": \"Заряд\",\n  \"message.gadget.invalidblock\": \"Неверный блок\",\n  \"message.gadget.toolmode\": \"Режим инструмента\",\n  \"message.gadget.toolrange\": \"Диапозон инструмента\",\n  \"message.gadget.anchorrender\": \"Отобразить привязку\",\n  \"message.gadget.anchorremove\": \"Убрать привязку\",\n  \"message.gadget.nothingtoundo\": \"Нечего отменять\",\n  \"message.gadget.undofailed\": \"Отмена невозможна (Слишком далеко?)\",\n  \"message.gadget.fuzzymode\": \"Режим 'Все подряд'\",\n  \"item.buildinggadgets.constructionpastecontainer\": \"Контейнер пасты\",\n  \"item.buildinggadgets.constructionpaste\": \"Строительная паста\",\n  \"tooltip.pasteContainer.amount\": \"Количество\",\n  \"block.buildinggadgets.constructionblock\": \"Строительный блок\",\n  \"block.buildinggadgets.constructionblockpowder\": \"Пыль строительного блока\",\n  \"tooltip.constructionblockpowder.helptext\": \"Ставить вблизи воды\"\n}"
  },
  {
    "path": "src/main/resources/assets/buildinggadgets/lang/uk_ua.json",
    "content": "{\n  \"_comment\": \"інтерфейс - довідка:\",\n  \"itemGroup.buildinggadgets\": \"Будівничі ґаджети\",\n  \"item.buildinggadgets.gadget_building\": \"Ґаджет будівництва\",\n  \"item.buildinggadgets.gadget_copy_paste\": \"Ґаджет копіювання і вставки\",\n  \"item.buildinggadgets.gadget_destruction\": \"Ґаджет руйнування\",\n  \"item.buildinggadgets.gadget_exchanging\": \"Ґаджет заміни\",\n  \"item.buildinggadgets.construction_paste\": \"Будівельна паста\",\n  \"item.buildinggadgets.construction_chunk_dense\": \"Щільний шматочок пасти\",\n  \"item.buildinggadgets.construction_paste_container_t1\": \"Контейтер пасти\",\n  \"item.buildinggadgets.construction_paste_container_t2\": \"Контейтер пасти Р2\",\n  \"item.buildinggadgets.construction_paste_container_t3\": \"Контейтер пасти Р3\",\n  \"item.buildinggadgets.construction_paste_container_creative\": \"Творчий контейтер пасти\",\n  \"item.buildinggadgets.template\": \"Шаблон\",\n  \"block.buildinggadgets.construction_block\": \"Щільна паста\",\n  \"block.buildinggadgets.construction_block_dense\": \"Щільний шматочок пасти\",\n  \"block.buildinggadgets.construction_block_powder\": \"Порошок будівельного блоку\",\n  \"block.buildinggadgets.effect_block\": \"(Не використовується) Блок ефектів\",\n  \"block.buildinggadgets.template_manager\": \"Менеджер шаблонів\",\n  \"block.buildinggadgets.charging_station\": \"Зарядна станція\",\n  \"key.buildinggadgets.category\": \"Будівничі ґаджети\",\n  \"key.buildinggadgets.anchor\": \"Якір\",\n  \"key.buildinggadgets.settings_menu\": \"Меню налаштувань\",\n  \"key.buildinggadgets.range\": \"Діапазон / Накладення\",\n  \"key.buildinggadgets.rotate_mirror\": \"Обертати / Відзеркалити\",\n  \"key.buildinggadgets.undo\": \"Скасувати\",\n  \"key.buildinggadgets.fuzzy\": \"Нечіткість\",\n  \"key.buildinggadgets.connected_area\": \"Під’єднана зона\",\n  \"key.buildinggadgets.material_list\": \"Відкрити список матеріалів\",\n  \"tooltip.constructionblockpowder.helptext\": \"Розмістити поруч з водою\",\n  \"tooltip.gadget.block\": \"Блок: %s\",\n  \"tooltip.gadget.destroyshowoverlay\": \"Показувати накладення: %s\",\n  \"tooltip.gadget.destroywarning\": \"УВАГА: Цей інструмент знищує блоки.\",\n  \"tooltip.gadget.energy\": \"Енергія: %s/%s\",\n  \"tooltip.gadget.mode\": \"Режим: %s\",\n  \"tooltip.gadget.rotate\": \"Обернути\",\n  \"tooltip.gadget.mirror\": \"Дзеркало\",\n  \"tooltip.gadget.range\": \"Діапазон: %s (%s блоків)\",\n  \"tooltip.gadget.anchor\": \"Якір\",\n  \"tooltip.gadget.undo\": \"Скасувати\",\n  \"tooltip.gadget.fuzzy\": \"Нечіткість: %s\",\n  \"tooltip.gadget.raytrace_fluid\": \"Рідини: %s\",\n  \"tooltip.gadget.building.place_atop\": \"Розташувати зверху: %s\",\n  \"tooltip.gadget.connected\": \"Під'єднано %s\",\n  \"tooltip.gadget.connected_area\": \"Під’єднана зона: %s\",\n  \"tooltip.gadget.connected_surface\": \"Під’єднана поверхня\",\n  \"tooltip.pasteContainer.amount\": \"Кількість: %d / %d\",\n  \"tooltip.pasteContainer.creative.amountMsg\": \"Кількість: Нескінченно!\",\n  \"tooltip.charger.energy\": \"Енергія: %s FE\",\n  \"tooltip.charger.burn_time\": \"Залишилось часу горіти: %sс\",\n  \"tooltip.charger.fuel_empty\": \"Немає палива\",\n  \"tooltip.template.name\": \"Ім'я: %s\",\n  \"tooltip.template.author\": \"Автор: %s\",\n  \"tooltip.donotuse\": \"НЕ ВИКОРИСТОВУЄТЬСЯ (WIP)\",\n  \"buildinggadgets.commands.force_unloaded.no_player\": \"Не можна перемкнути статус гравця без статусу вивантаження гравця для цілі!\",\n  \"buildinggadgets.commands.force_unloaded.toggled\": \"Встановлення ForceLoadedChunks для гравця %s на %b.\",\n  \"buildinggadgets.commands.force_unloaded.list\": \"Гравець з UUID %s має ForceLoadedChunks встановлено на %b.\",\n  \"buildinggadgets.commands.override_copy_size.no_player\": \"Не можна перемкнути статус гравця без копіювання статусу гравця для цілі!\",\n  \"buildinggadgets.commands.override_copy_size.toggled\": \"Встановлення OverrideCopySize для гравця %s на %b.\",\n  \"buildinggadgets.commands.override_copy_size.list\": \"Гравець з UUID %s має OverrideCopySize встановлено на %b.\",\n  \"buildinggadgets.commands.override_build_size.no_player\": \"Не можна перемкнути статус гравця без побудованого статусу гравця для цілі!\",\n  \"buildinggadgets.commands.override_build_size.toggled\": \"Встановлення OverrideBuildSize для гравця %s на %b.\",\n  \"buildinggadgets.commands.override_build_size.list\": \"Гравець з UUID %s має OverrideBuildSize встановлено на %b.\",\n  \"buildinggadgets.radialmenu.destruction_overlay\": \"Показувати накладення\",\n  \"buildinggadgets.radialmenu.fluid_only\": \"Режим лише рідини\",\n  \"buildinggadgets.radialmenu.rotate\": \"Обернути\",\n  \"buildinggadgets.radialmenu.mirror\": \"Дзеркало\",\n  \"buildinggadgets.radialmenu.fuzzy\": \"Нечіткість\",\n  \"buildinggadgets.radialmenu.connected_area\": \"Під’єднана зона\",\n  \"buildinggadgets.radialmenu.connected_surface\": \"Під’єднана зона\",\n  \"buildinggadgets.radialmenu.open_gui\": \"Відкрити інтерфейс\",\n  \"buildinggadgets.radialmenu.open_material-list\": \"Відкрити список матеріалів\",\n  \"buildinggadgets.radialmenu.raytrace_fluid\": \"Рідини\",\n  \"buildinggadgets.radialmenu.place_on_top\": \"Розташувати зверху\",\n  \"buildinggadgets.radialmenu.anchor\": \"Якір\",\n  \"buildinggadgets.radialmenu.undo\": \"Скасувати\",\n  \"buildinggadgets.modes.surface\": \"Поверхня\",\n  \"buildinggadgets.modes.grid\": \"Сітка\",\n  \"buildinggadgets.modes.horizontal_column\": \"Горизонтальна колона\",\n  \"buildinggadgets.modes.horizontal_wall\": \"Горизонтальна стіна\",\n  \"buildinggadgets.modes.vertical_column\": \"Вертикальна колона\",\n  \"buildinggadgets.modes.vertical_wall\": \"Вертикальна стіна\",\n  \"buildinggadgets.modes.stairs\": \"Сходи\",\n  \"buildinggadgets.modes.build_to_me\": \"До мене\",\n  \"buildinggadgets.modes.copy\": \"Копіювати\",\n  \"buildinggadgets.modes.paste\": \"Вставити\",\n  \"buildinggadgets.message.copy_unloaded\": \"Спроба скопіювати чанки (%d штук). Якщо ви все ще хочете скопіювати, використайте команду ForceLoadChunks.\",\n  \"buildinggadgets.message.undo_unloaded\": \"Спроба скасувати операцію, яка охопила незавантажені чанки (%d штук). Якщо ви все ще хочете скасувати, використайте команду ForceLoadChunks.\",\n  \"buildinggadgets.message.build_unloaded\": \"Спроба побудувати щось, що охопить незавантажені чанки (%d штук). Якщо ви все ще хочете побудувати, використайте команду ForceLoadChunks.\",\n  \"buildinggadgets.message.copy_too_large\": \"Спроба копіювання об'єкту розміром (%d, %d, %d), що перевищує максимальний розмір, визначений на вашому сервері (%d, %d, %d). Якщо ви все ж хочете скопіювати, будь ласка, скористайтеся командою OverrideCopySize.\",\n  \"buildinggadgets.message.build_too_large\": \"Спроба створення будівлі розміром (%d, %d, %d) більше, ніж максимальний розмір, визначений на вашому сервері (%d, %d, %d). Якщо ви все ж хочете побудувати, будь ласка, скористайтеся командою OverrideBuildSize.\",\n  \"buildinggadgets.message.server_busy\": \"Сервер зайнятий. Будь ласка, зачекайте.\",\n  \"buildinggadgets.message.gadget_busy\": \"Ґаджет зайнятий. Будь ласка, зачекайте.\",\n  \"buildinggadgets.message.area_too_big\": \"Зона занадто велика, максимальна зона - 65 536 X 256 X 65 536. Клацніть правою кнопкою миші на порожньому повітрі, щоб скинути налаштування\",\n  \"buildinggadgets.message.too_many_blocks\": \"Занадто багато блоків, ліміт 2 147 483 647 (клацніть правою кнопкою миші на порожньому повітрі, щоб скинути)\",\n  \"buildinggadgets.message.too_many_dif_blocks\": \"Занадто багато різних блоків, ліміт 16 777 216 (клацніть правою кнопкою миші на порожньому повітрі, щоб скинути)\",\n  \"buildinggadgets.message.template_build\": \"Збудувати шаблон\",\n  \"buildinggadgets.message.copied\": \"Зона скопійована\",\n  \"buildinggadgets.message.not_copied\": \"Помилка копіювання зони\",\n  \"buildinggadgets.message.anchor_removed\": \"Якір видалено\",\n  \"buildinggadgets.message.anchor_set\": \"Якір розташовано\",\n  \"buildinggadgets.message.area_reset\": \"Скинути зону копіювання\",\n  \"buildinggadgets.message.copy_failed.template_write\": \"Не вдалося записати файл шаблону.\",\n  \"buildinggadgets.message.copy_failed.error\": \"Не вдалося скопіювати до буфера обміну.\",\n  \"buildinggadgets.message.copy_clipboard_success\": \"Копіювання в буфер обміну успішно\",\n  \"buildinggadgets.message.destroy_size_too_large\": \"Максимальний розмір руйнування становить %1$dx%1$dx%1$d\",\n  \"buildinggadgets.message.fuzzy_mode\": \"Режим нечіткості: %s\",\n  \"buildinggadgets.message.raytrace_fluid\": \"Рідини: %s\",\n  \"buildinggadgets.message.building.placement\": \"Розміщення блоків: %s\",\n  \"buildinggadgets.message.place.atop\": \"Зверху\",\n  \"buildinggadgets.message.place.inside\": \"Всередині\",\n  \"buildinggadgets.message.connected_area\": \"Під’єднана зона: %s\",\n  \"buildinggadgets.message.connected_surface\": \"Під’єднана поверхня: %s\",\n  \"buildinggadgets.message.invalid_block\": \"Некоректний блок (%s)\",\n  \"buildinggadgets.message.undo_missing_items\": \"Відсутні елементи для скасування\",\n  \"buildinggadgets.message.undo_failed\": \"Невдале скасування (надто далеко?)\",\n  \"buildinggadgets.message.nothing_to_undo\": \"Нічого скасувати\",\n  \"buildinggadgets.message.paste_failed\": \"Не вдалося вставити шаблон до буфера обміну.\",\n  \"buildinggadgets.message.paste_failed.wrong_mc_version\": \"Не вдалося вставити, версія майнкрафт %s не підтримується. Будь ласка, використовуйте лише шаблони, які було створено для (%s) -> (%s)\",\n  \"buildinggadgets.message.paste_failed.too_recent_version\": \"Помилка вставки, версія шаблону %s невідома для цього екземпляра. Будь ласка, використовуйте лише ті шаблони, які було створено до версії формату %s.\",\n  \"buildinggadgets.message.paste_failed.invalid_json\": \"Помилка вставки, неправильний JSON\",\n  \"buildinggadgets.message.paste_failed.corrupt_json\": \"Помилка вставки, пошкоджений JSON\",\n  \"buildinggadgets.message.paste_failed.corrupt_body\": \"Не вдалося вставити, тіло шаблону пошкоджено!\",\n  \"buildinggadgets.message.paste_failed.link_copied\": \"Не вдалося вставити дані. Будь ласка, скопіюйте необроблені json-дані замість посилання.\",\n  \"buildinggadgets.message.paste_success\": \"Вставлення з буферу обміну успішно\",\n  \"buildinggadgets.message.rotated\": \"Обертання блоків\",\n  \"buildinggadgets.message.mirrored\": \"Віддзеркалені блоки\",\n  \"buildinggadgets.message.tool_mode\": \"Режим інструмента: %s\",\n  \"buildinggadgets.message.range_set\": \"Діапозон інструментів: %d\",\n  \"buildinggadgets.message.boundTE\": \"Ґаджет пов'язаний з Інвентарем\",\n  \"buildinggadgets.message.unboundTE\": \"Неприв'язаний ґаджет з інвентарем\",\n  \"buildinggadgets.message.failed_to_bind\": \"Не вдалося прив'язати до інвентарю\",\n  \"buildinggadgets.message.invalid_inventory\": \"Цей блок не є коректним інвентарем\",\n  \"buildinggadgets.message.first_copy\": \"Перша позиція встановлена\",\n  \"buildinggadgets.easter_eggs.9x9.for_the_collection\": \"Ще 9 на 9 для занурення... Йдіть сабміть!\",\n  \"buildinggadgets.easter_eggs.9x9.just_like_dire\": \"9 на 9, подібно до \\\"Дір\\\".\",\n  \"buildinggadgets.easter_eggs.9x9.dire_would_be_proud\": \"Дір пишався б тобою.\",\n  \"buildinggadgets.easter_eggs.9x9.no_dire_wire\": \"Тільки без дротів, будь ласка.\",\n  \"buildinggadgets.easter_eggs.dire_9x9.another_one\": \"Ще один... Дійсно жахливо?!?\",\n  \"buildinggadgets.easter_eggs.dire_9x9.dont_tell\": \"Не кажіть своїм глядачам.\",\n  \"buildinggadgets.easter_eggs.dire_9x9.correct_size\": \"Ви впевнені, що це правильний розмір?\",\n  \"buildinggadgets.easter_eggs.dire_9x9.no_dire_wire\": \"Цього разу броні відсутні. Будь ласка.\",\n  \"gui.buildinggadgets.tm.button.copy\": \"Копіювати\",\n  \"gui.buildinggadgets.tm.button.paste\": \"Вставити\",\n  \"gui.buildinggadgets.tm.button.save\": \"Зберегти\",\n  \"gui.buildinggadgets.tm.button.load\": \"Завантажено\",\n  \"gui.buildinggadgets.tm.name_field.text\": \"ім'я?\",\n  \"gui.buildinggadgets.tm.field.placeholder\": \"Назва шаблону\",\n  \"gui.buildinggadgets.single.confirm\": \"Підтвердити\",\n  \"gui.buildinggadgets.single.cancel\": \"Скасувати\",\n  \"gui.buildinggadgets.single.close\": \"Закрити\",\n  \"gui.buildinggadgets.single.clear\": \"Очистити\",\n  \"gui.buildinggadgets.single.reset\": \"Скинути\",\n  \"gui.buildinggadgets.single.range\": \"Діапозон\",\n  \"gui.buildinggadgets.field.start\": \"Почати\",\n  \"gui.buildinggadgets.field.end\": \"Кінець\",\n  \"gui.buildinggadgets.copy.button.absolute\": \"Абсолютні координати\",\n  \"gui.buildinggadgets.copy.label.heading\": \"Внести коригування\",\n  \"gui.buildinggadgets.copy.label.subheading\": \"Використовуйте абсолютний режим, щоб використовувати розташування блоків.\",\n  \"gui.buildinggadgets.destruction.field.depth\": \"Глибина\",\n  \"gui.buildinggadgets.destruction.field.down\": \"Вниз\",\n  \"gui.buildinggadgets.destruction.field.left\": \"Вліво\",\n  \"gui.buildinggadgets.destruction.field.right\": \"Вправо\",\n  \"gui.buildinggadgets.destruction.field.up\": \"Вгору\",\n  \"gui.buildinggadgets.materialList.button.close\": \"Закрити\",\n  \"gui.buildinggadgets.materialList.button.copyList\": \"Копіювати\",\n  \"gui.buildinggadgets.materialList.button.sorting.nameAZ\": \"А-Я\",\n  \"gui.buildinggadgets.materialList.button.sorting.nameZA\": \"Я-А\",\n  \"gui.buildinggadgets.materialList.button.sorting.requiredAcse\": \"Необхідно ↓\",\n  \"gui.buildinggadgets.materialList.button.sorting.requiredDesc\": \"Необхідно ↓\",\n  \"gui.buildinggadgets.materialList.button.sorting.missingAcse\": \"Бракує ↓\",\n  \"gui.buildinggadgets.materialList.button.sorting.missingDesc\": \"Бракує ↑\",\n  \"gui.buildinggadgets.materialList.message.copyList.success\": \"Список матеріалів скопійовано\",\n  \"gui.buildinggadgets.materialList.titleEmpty\": \"Список матеріалів\",\n  \"gui.buildinggadgets.materialList.titleNameOnly\": \"Список матеріалів для %s\",\n  \"gui.buildinggadgets.materialList.titleAuthorOnly\": \"Список матеріалів від %s\",\n  \"gui.buildinggadgets.materialList.title\": \"Список матеріалів для %s від %s\",\n  \"gui.buildinggadgets.materialList.help.copyList\": \"Скопіюйте текстову версію списку матеріалів.\\nНатисніть Ctrl / Command, щоб створити детальну версію з кодовими назвами\",\n  \"gui.buildinggadgets.help.area.arrow.data_flow\": \"Представляє потік даних з одного елементу до іншого; або від шаблону до ґаджета/шаблону, або від ґаджета/шаблону до шаблону чи аркуша паперу\",\n  \"gui.buildinggadgets.help.area.field.template_name\": \"Коли дані зберігаються/вставляються, шаблон матиме це ім'я\",\n  \"gui.buildinggadgets.help.area.preview\": \"Попередній перегляд даних, що зберігаються в ґаджеті/шаблоні\",\n  \"gui.buildinggadgets.help.area.slot.gadget\": \"Приймає §nґаджет копіювання і вставки§r або §nшаблон§r для збереження / копіювання з та завантаження у\\\\n\\\\n§9Кольорове накладення§r показує, що елемент отримає дані\",\n  \"gui.buildinggadgets.help.area.slot.template\": \"Приймає §нпапір§р або §nшаблон§r для завантаження та збереження/вставки\\\\n\\\\n§9Кольорове накладення§r показує, що цей елемент отримає дані\",\n  \"gui.buildinggadgets.help.button.copy\": \"Копіює дані з ґаджету/шаблону в буфер обміну комп'ютера\",\n  \"gui.buildinggadgets.help.button.help.enter\": \"Показати довідку з наведенням\",\n  \"gui.buildinggadgets.help.button.help.exit\": \"Вийти з режиму довідки\",\n  \"gui.buildinggadgets.help.button.load\": \"Завантажує дані до ґаджету/шаблону з шаблону\",\n  \"gui.buildinggadgets.help.button.paste\": \"Копіює дані з буферу обміну комп'ютера до попереднього шаблону або до аркуша паперу, створюючи шаблон\",\n  \"gui.buildinggadgets.help.button.save\": \"Зберігає дані в ґаджет/шаблон до попереднього шаблону або до аркуша паперу, створюючи шаблон\"\n}\n"
  },
  {
    "path": "src/main/resources/assets/buildinggadgets/lang/zh_cn.json",
    "content": "{\n  \"_comment\": \"itemGroup:\",\n  \"itemGroup.buildinggadgets\": \"建筑小帮手\",\n  \"_comment\": \"item:\",\n  \"item.buildinggadgets.gadget_building\": \"建筑小帮手\",\n  \"item.buildinggadgets.gadget_copy_paste\": \"复制粘贴小帮手\",\n  \"item.buildinggadgets.gadget_destruction\": \"破坏小帮手\",\n  \"item.buildinggadgets.gadget_exchanging\": \"更替小帮手\",\n  \"item.buildinggadgets.construction_paste\": \"构建黏膏\",\n  \"item.buildinggadgets.construction_chunk_dense\": \"致密构建块\",\n  \"item.buildinggadgets.construction_paste_container_t1\": \"黏膏容器\",\n  \"item.buildinggadgets.construction_paste_container_t2\": \"黏膏容器 T2\",\n  \"item.buildinggadgets.construction_paste_container_t3\": \"黏膏容器 T3\",\n  \"item.buildinggadgets.construction_paste_container_creative\": \"创造黏膏容器\",\n  \"item.buildinggadgets.template\": \"模板\",\n  \"_comment\": \"block:\",\n  \"block.buildinggadgets.construction_block\": \"构建方块\",\n  \"block.buildinggadgets.construction_block_dense\": \"致密构建方块\",\n  \"block.buildinggadgets.construction_block_powder\": \"构建方块粉末\",\n  \"block.buildinggadgets.effect_block\": \"（未使用）特效方块\",\n  \"block.buildinggadgets.template_manager\": \"模板管理器\",\n  \"block.buildinggadgets.charging_station\": \"充能站\",\n  \"_comment\": \"key:\",\n  \"key.buildinggadgets.category\": \"建筑小帮手\",\n  \"key.buildinggadgets.anchor\": \"锚定点\",\n  \"key.buildinggadgets.settings_menu\": \"设置菜单\",\n  \"key.buildinggadgets.range\": \"范围 / 覆盖\",\n  \"key.buildinggadgets.rotate_mirror\": \"旋转 / 镜像\",\n  \"key.buildinggadgets.undo\": \"撤销\",\n  \"key.buildinggadgets.fuzzy\": \"模糊\",\n  \"key.buildinggadgets.connected_area\": \"连接区域\",\n  \"key.buildinggadgets.material_list\": \"打开材料列表\",\n  \"_comment\": \"tooltip:\",\n  \"tooltip.constructionblockpowder.helptext\": \"放置在水旁\",\n  \"tooltip.gadget.block\": \"方块: %s\",\n  \"tooltip.gadget.destroyshowoverlay\": \"显示覆盖: %s\",\n  \"tooltip.gadget.destroywarning\": \"警告：这个工具会直接销毁方块。\",\n  \"tooltip.gadget.energy\": \"能量: %s/%s\",\n  \"tooltip.gadget.mode\": \"模式: %s\",\n  \"tooltip.gadget.rotate\": \"旋转\",\n  \"tooltip.gadget.mirror\": \"镜像\",\n  \"tooltip.gadget.range\": \"范围: %s (%s 个方块)\",\n  \"tooltip.gadget.anchor\": \"锚定点\",\n  \"tooltip.gadget.undo\": \"撤销\",\n  \"tooltip.gadget.fuzzy\": \"模糊: %s\",\n  \"tooltip.gadget.raytrace_fluid\": \"流体跟踪: %s\",\n  \"tooltip.gadget.building.place_atop\": \"顶部放置: %s\",\n  \"tooltip.gadget.connected\": \"连接 %s\",\n  \"tooltip.gadget.connected_area\": \"连接区域: %s\",\n  \"tooltip.gadget.connected_surface\": \"连接面\",\n  \"tooltip.pasteContainer.amount\": \"数量: %d\",\n  \"tooltip.pasteContainer.creative.amountMsg\": \"数量: 无限!\",\n  \"tooltip.charger.energy\": \"能量: %s FE\",\n  \"tooltip.charger.burn_time\": \"剩余燃烧时间: %ss\",\n  \"tooltip.charger.fuel_empty\": \"无燃料\",\n  \"tooltip.template.name\": \"名字: %s\",\n  \"tooltip.template.author\": \"作者: %s\",\n  \"tooltip.donotuse\": \"不要使用(未完成)\",\n  \"_comment\": \"commands\",\n  \"buildinggadgets.commands.force_unloaded.no_player\": \"Cannot toggle player status without a player unload status to target!\",\n  \"buildinggadgets.commands.force_unloaded.toggled\": \"将玩家 %s 的ForceLoadedChunks设置为 %b\",\n  \"buildinggadgets.commands.force_unloaded.list\": \"UUID为 %s 的玩家已将ForceLoadedChunks设置为 %b\",\n  \"buildinggadgets.commands.override_copy_size.no_player\": \"Cannot toggle player status without a player copy status to target!\",\n  \"buildinggadgets.commands.override_copy_size.toggled\": \"将玩家 %s 的OverrideCopySize设置为 %b\",\n  \"buildinggadgets.commands.override_copy_size.list\": \"UUID为 %s 的玩家已将OverrideCopySize设置为 %b\",\n  \"buildinggadgets.commands.override_build_size.no_player\": \"Cannot toggle player status without a player build status to target!\",\n  \"buildinggadgets.commands.override_build_size.toggled\": \"将玩家 %s 的OverrideBuildSize设置为 %b\",\n  \"buildinggadgets.commands.override_build_size.list\": \"UUID为 %s 的玩家已将OverrideBuildSize设置为 %b\",\n  \"_comment\": \"radial-menu\",\n  \"buildinggadgets.radialmenu.destruction_overlay\": \"显示覆盖\",\n  \"buildinggadgets.radialmenu.rotate\": \"旋转\",\n  \"buildinggadgets.radialmenu.mirror\": \"镜像\",\n  \"buildinggadgets.radialmenu.fuzzy\": \"模糊\",\n  \"buildinggadgets.radialmenu.connected_area\": \"连接区域\",\n  \"buildinggadgets.radialmenu.connected_surface\": \"连接面\",\n  \"buildinggadgets.radialmenu.open_gui\": \"打开GUI\",\n  \"buildinggadgets.radialmenu.raytrace_fluid\": \"流体跟踪\",\n  \"buildinggadgets.radialmenu.place_on_top\": \"顶部放置\",\n  \"buildinggadgets.radialmenu.anchor\": \"锚定点\",\n  \"buildinggadgets.radialmenu.undo\": \"撤销\",\n  \"_comment\": \"modes:\",\n  \"buildinggadgets.modes.surface\": \"平面\",\n  \"buildinggadgets.modes.grid\": \"网格\",\n  \"buildinggadgets.modes.horizontal_column\": \"水平柱体\",\n  \"buildinggadgets.modes.horizontal_wall\": \"水平墙面\",\n  \"buildinggadgets.modes.vertical_column\": \"纵向柱体\",\n  \"buildinggadgets.modes.vertical_wall\": \"纵向墙面\",\n  \"buildinggadgets.modes.stairs\": \"楼梯\",\n  \"buildinggadgets.modes.build_to_me\": \"建筑到玩家所在位置\",\n  \"buildinggadgets.modes.copy\": \"复制\",\n  \"buildinggadgets.modes.paste\": \"粘贴\",\n  \"_comment\": \"message:\",\n  \"buildinggadgets.message.copy_unloaded\": \"Attempted to perform a copy which would have enclosed %d unloaded chunks. If you want to copy nonetheless, please use the ForceLoadChunks command.\",\n  \"buildinggadgets.message.undo_unloaded\": \"Attempted to perform an undo which would have enclosed %d unloaded chunks. If you want to undo nonetheless, please use the ForceLoadChunks command.\",\n  \"buildinggadgets.message.build_unloaded\": \"Attempted to perform a build which would have enclosed %d unloaded chunks. If you want to build nonetheless, please use the ForceLoadChunks command.\",\n  \"buildinggadgets.message.copy_too_large\": \"Attempted to perform a copy of size (%d, %d, %d) which is larger then your server's defined max size of (%d, %d, %d). If you want to copy nonetheless, please use the OverrideCopySize command.\",\n  \"buildinggadgets.message.build_too_large\": \"Attempted to perform a build of size (%d, %d, %d) which is larger then your server's defined max size of (%d, %d, %d). If you want to build nonetheless, please use the OverrideBuildSize command.\",\n  \"buildinggadgets.message.server_busy\": \"The Server is currently Busy. Please Wait.\",\n  \"buildinggadgets.message.gadget_busy\": \"The Gadget is currently Busy. Please Wait.\",\n  \"buildinggadgets.message.area_too_big\": \"Area too Large, max area is 65 536 X 256 X 65 536 (Right click empty air to reset)\",\n  \"buildinggadgets.message.too_many_blocks\": \"方块过多，最大只允许 2,147,483,647个（右击空气进行重置）\",\n  \"buildinggadgets.message.too_many_dif_blocks\": \"不同的方块过多，最大只允许 2,147,483,647个（右击空气进行重置）\",\n  \"buildinggadgets.message.template_build\": \"模板已构建\",\n  \"buildinggadgets.message.copied\": \"区域已复制\",\n  \"buildinggadgets.message.not_copied\": \"复制区域失败\",\n  \"buildinggadgets.message.anchor_removed\": \"锚定点已移除\",\n  \"buildinggadgets.message.anchor_set\": \"锚定点已渲染\",\n  \"buildinggadgets.message.area_reset\": \"复制区域重置\",\n  \"buildinggadgets.message.copy_failed.template_write\": \"无法写入模板二进制文件\",\n  \"buildinggadgets.message.copy_failed.error\": \"无法复制到剪贴板\",\n  \"buildinggadgets.message.copy_clipboard_success\": \"复制到剪贴板成功\",\n  \"buildinggadgets.message.destroy_size_too_large\": \"最大破坏大小为%1$dx%1$dx%1$d，负数无效\",\n  \"buildinggadgets.message.fuzzy_mode\": \"模糊模式: %s\",\n  \"buildinggadgets.message.raytrace_fluid\": \"流体跟踪: %s\",\n  \"buildinggadgets.message.building.placement\": \"方块放置: %s\",\n  \"buildinggadgets.message.place.atop\": \"顶部\",\n  \"buildinggadgets.message.place.inside\": \"内部\",\n  \"buildinggadgets.message.connected_area\": \"连接区域: %s\",\n  \"buildinggadgets.message.connected_surface\": \"连接面: %s\",\n  \"buildinggadgets.message.invalid_block\": \"无效方块 (%s)\",\n  \"buildinggadgets.message.undo_missing_items\": \"缺失撤销的物品\",\n  \"buildinggadgets.message.undo_failed\": \"撤销失败，尝试靠近一点？\",\n  \"buildinggadgets.message.nothing_to_undo\": \"没有需要撤销的操作\",\n  \"buildinggadgets.message.paste_failed\": \"无法将模板粘贴到剪贴板\",\n  \"buildinggadgets.message.paste_failed.wrong_mc_version\": \"粘贴失败，不支持MC版本%s，请使用当前版本构建的模板(%s)\",\n  \"buildinggadgets.message.paste_failed.too_recent_version\": \"粘贴失败，这个例子的模板版本 %s 是未知的，请使用已构建为 %s 版本的模板\",\n  \"buildinggadgets.message.paste_failed.invalid_json\": \"粘贴失败，无效的JSON\",\n  \"buildinggadgets.message.paste_failed.corrupt_json\": \"粘贴失败，损坏的JSON\",\n  \"buildinggadgets.message.paste_failed.corrupt_body\": \"粘贴失败，模板主体损坏!\",\n  \"buildinggadgets.message.paste_failed.link_copied\": \"粘贴失败，请复制原始JSON数据，而不是链接\",\n  \"buildinggadgets.message.paste_success\": \"从剪贴板粘贴成功\",\n  \"buildinggadgets.message.rotated\": \"方块已旋转\",\n  \"buildinggadgets.message.mirrored\": \"方块已镜像\",\n  \"buildinggadgets.message.tool_mode\": \"工具模式: %s\",\n  \"buildinggadgets.message.range_set\": \"工具范围: %d\",\n  \"buildinggadgets.message.boundTE\": \"绑定小帮手到物品栏\",\n  \"buildinggadgets.message.unboundTE\": \"从物品栏解绑小帮手\",\n  \"buildinggadgets.message.first_copy\": \"设置第一个位置\",\n  \"buildinggadgets.easter_eggs.9x9.for_the_collection\": \"Another 9 by 9 for Dire'S collection... Go submit it!\",\n  \"buildinggadgets.easter_eggs.9x9.just_like_dire\": \"A 9 by 9 just like Dire's.\",\n  \"buildinggadgets.easter_eggs.9x9.dire_would_be_proud\": \"Dire would be proud.\",\n  \"buildinggadgets.easter_eggs.9x9.no_dire_wire\": \"No Dire-Wire please.\",\n  \"buildinggadgets.easter_eggs.dire_9x9.another_one\": \"Another one... Really Dire?!?\",\n  \"buildinggadgets.easter_eggs.dire_9x9.dont_tell\": \"Don't tell your viewers.\",\n  \"buildinggadgets.easter_eggs.dire_9x9.correct_size\": \"You sure that's the correct size?\",\n  \"buildinggadgets.easter_eggs.dire_9x9.no_dire_wire\": \"No Dire-Wire this time. Please.\",\n  \"_comment\": \"gui - template manager\",\n  \"gui.buildinggadgets.tm.button.copy\": \"复制\",\n  \"gui.buildinggadgets.tm.button.paste\": \"粘贴\",\n  \"gui.buildinggadgets.tm.button.save\": \"保存\",\n  \"gui.buildinggadgets.tm.button.load\": \"加载\",\n  \"gui.buildinggadgets.tm.name_field.text\": \"名字？\",\n  \"_comment\": \"gui - single words\",\n  \"gui.buildinggadgets.single.confirm\": \"确认\",\n  \"gui.buildinggadgets.single.cancel\": \"取消\",\n  \"gui.buildinggadgets.single.close\": \"关闭\",\n  \"gui.buildinggadgets.single.clear\": \"清除\",\n  \"gui.buildinggadgets.single.reset\": \"重置\",\n  \"_comment\": \"gui - destruction:\",\n  \"gui.buildinggadgets.copy.button.absolute\": \"使用绝对坐标\",\n  \"gui.buildinggadgets.copy.label.heading\": \"调整选项\",\n  \"gui.buildinggadgets.copy.label.subheading\": \"使用绝对坐标选择方块位置\",\n  \"gui.buildinggadgets.destruction.field.depth\": \"深度\",\n  \"gui.buildinggadgets.destruction.field.down\": \"下\",\n  \"gui.buildinggadgets.destruction.field.left\": \"左\",\n  \"gui.buildinggadgets.destruction.field.right\": \"右\",\n  \"gui.buildinggadgets.destruction.field.up\": \"上\",\n  \"_comment\": \"gui - material list:\",\n  \"gui.buildinggadgets.materialList.button.close\": \"关闭\",\n  \"gui.buildinggadgets.materialList.button.copyList\": \"复制\",\n  \"gui.buildinggadgets.materialList.button.sorting.nameAZ\": \"A-Z\",\n  \"gui.buildinggadgets.materialList.button.sorting.nameZA\": \"Z-A\",\n  \"gui.buildinggadgets.materialList.button.sorting.requiredAcse\": \"需要 ↓\",\n  \"gui.buildinggadgets.materialList.button.sorting.requiredDesc\": \"需要 ↑\",\n  \"gui.buildinggadgets.materialList.button.sorting.missingAcse\": \"缺失 ↓\",\n  \"gui.buildinggadgets.materialList.button.sorting.missingDesc\": \"缺失 ↑\",\n  \"gui.buildinggadgets.materialList.message.copyList.success\": \"成功复制材料列表\",\n  \"gui.buildinggadgets.materialList.titleEmpty\": \"材料列表\",\n  \"gui.buildinggadgets.materialList.titleNameOnly\": \"%s 的材料列表\",\n  \"gui.buildinggadgets.materialList.titleAuthorOnly\": \"来自 %s 的材料列表\",\n  \"gui.buildinggadgets.materialList.title\": \"%s 的材料列表 来自 %s\",\n  \"gui.buildinggadgets.materialList.help.copyList\": \"复制材料列表的文本版本。\\n按Ctrl键/使用命令创建具有代码名称的详细版本\",\n  \"_comment\": \"gui - help:\",\n  \"gui.buildinggadgets.help.area.arrow.data_flow\": \"表示从一个物品到另一个物品的数据流；从模板到小帮手/模板，或从小帮手/模板到模板或一张纸\",\n  \"gui.buildinggadgets.help.area.field.template_name\": \"保存/粘贴数据时，生成的模板将具有此名称\",\n  \"gui.buildinggadgets.help.area.preview\": \"预览存储在小帮手/模板中的数据\",\n  \"gui.buildinggadgets.help.area.slot.gadget\": \"Accepts §ncopy-paste gadget§r or §ntemplate§r to be saved/copied from and loaded to \\\\n\\\\n§9Colored overlay§r indicates that the item will receive data\",\n  \"gui.buildinggadgets.help.area.slot.template\": \"Accepts §npaper§r or §ntemplate§r to be loaded from and saved/pasted to\\\\n\\\\n§9Colored overlay§r indicates that the item will receive data\",\n  \"gui.buildinggadgets.help.button.copy\": \"将小帮手/模板的数据复制到你的计算机的剪贴板里\",\n  \"gui.buildinggadgets.help.button.help.enter\": \"显示悬浮帮助文本\",\n  \"gui.buildinggadgets.help.button.help.exit\": \"退出帮助模式\",\n  \"gui.buildinggadgets.help.button.load\": \"从模板将数据加载到小帮手/模板\",\n  \"gui.buildinggadgets.help.button.paste\": \"将计算机剪贴板上的数据粘贴到预先存在的模板或一张纸上，以此来创建模板\",\n  \"gui.buildinggadgets.help.button.save\": \"将小帮手/模板的数据保存到预先存在的模板或一张纸上，以此来创建模板\",\n  \"_comment\": \"config:\",\n  \"config.buildinggadgets.general.rayTraceRange\": \"最大构建距离\",\n  \"config.buildinggadgets.general.rayTraceRange.tooltip\": \"设置你可以构建的最远距离\",\n  \"config.buildinggadgets.general.poweredByFE\": \"由Forge Energy供能\",\n  \"config.buildinggadgets.general.poweredByFE.tooltip\": \"设置为 true 将使用 Forge Energy ，设置为 false 则为原版耐久损耗\",\n  \"config.buildinggadgets.general.paste.enabled\": \"启用构建黏膏\",\n  \"config.buildinggadgets.general.paste.enabled.tooltip\": \"设置为false则禁用建筑黏膏配方\",\n  \"config.buildinggadgets.general.paste.dropped.min\": \"构建黏膏掉落数量 - 最小\",\n  \"config.buildinggadgets.general.paste.dropped.min.tooltip\": \"由一个致密构建方块掉落的构建黏膏的最小数量\",\n  \"config.buildinggadgets.general.paste.dropped.max\": \"构建黏膏掉落数量 - 最大\",\n  \"config.buildinggadgets.general.paste.dropped.max.tooltip\": \"由一个致密构建方块掉落的构建黏膏的最大数量\",\n  \"config.buildinggadgets.general.enableDestructionGadget\": \"启用破坏小帮手\",\n  \"config.buildinggadgets.general.enableDestructionGadget.tooltip\": \"设置为 false 则禁用破坏小帮手\",\n  \"config.buildinggadgets.general.absoluteCoordDefault\": \"默认为绝对坐标模式\",\n  \"config.buildinggadgets.general.absoluteCoordDefault.tooltip\": \"在默认情况下，复制/粘贴 GUI 中坐标以“绝对”方式起始。\\n设置为 true 为绝对模式，设置为 False 为相对模式\",\n  \"config.buildinggadgets.general.canOverwriteBlocks\": \"允许覆盖非空气方块\",\n  \"config.buildinggadgets.general.canOverwriteBlocks.tooltip\": \"建筑/复制粘贴小帮手是否能够覆盖诸如水、熔岩、草之类的方块（就像玩家在放置一样）。\\n设置为 False 将只允许覆盖空气方块\",\n  \"config.buildinggadgets.general.subCategoryBlacklist\": \"黑名单设置\",\n  \"config.buildinggadgets.general.subCategoryBlacklist.tooltip\": \"在此处配置黑名单设置\",\n  \"config.buildinggadgets.general.subCategoryBlacklist.blockBlacklist\": \"黑名单方块\",\n  \"config.buildinggadgets.general.subCategoryBlacklist.blockBlacklist.tooltip\": \"所有添加到此处的方块都会被当做 TileEntities 一样处理。\\n你可以使用 Java 所使用的标准正则表达式来实现更复杂的过滤。\\n比如使用“awfulmod:.*”来将所有 awfulmod 模组方块加入黑名单\",\n  \"config.buildinggadgets.general.subCategoryGadgets\": \"小帮手\",\n  \"config.buildinggadgets.general.subCategoryGadgets.tooltip\": \"设置小帮手\",\n  \"config.buildinggadgets.general.subCategoryGadgets.maxRange\": \"最大允许范围\",\n  \"config.buildinggadgets.general.subCategoryGadgets.maxRange.tooltip\": \"小帮手最大允许的范围\",\n  \"config.buildinggadgets.general.subCategoryGadgets.maxEnergy\": \"最大能量\",\n  \"config.buildinggadgets.general.subCategoryGadgets.maxEnergy.tooltip\": \"建筑、更替和复制粘贴小帮手的最大能量\",\n  \"config.buildinggadgets.general.subCategoryGadgets.energyCost\": \"能量消耗\",\n  \"config.buildinggadgets.general.subCategoryGadgets.energyCost.tooltip\": \"每次操作，小帮手消耗的能量\",\n  \"config.buildinggadgets.general.subCategoryGadgets.damageCost\": \"耐久消耗\",\n  \"config.buildinggadgets.general.subCategoryGadgets.damageCost.tooltip\": \"每次操作，小帮手消耗的耐久\",\n  \"config.buildinggadgets.general.subCategoryGadgets.durability\": \"耐久\",\n  \"config.buildinggadgets.general.subCategoryGadgets.durability.tooltip\": \"小帮手的耐久（0 表示不使用耐用性）（如果开启 FE 供能模式忽略此项）\",\n  \"config.buildinggadgets.general.subCategoryGadgets.gadgetBuilding\": \"建筑小帮手\",\n  \"config.buildinggadgets.general.subCategoryGadgets.gadgetBuilding.tooltip\": \"建筑小帮手的能量消耗与耐久\",\n  \"config.buildinggadgets.general.subCategoryGadgets.gadgetExchanger\": \"更替小帮手\",\n  \"config.buildinggadgets.general.subCategoryGadgets.gadgetExchanger.tooltip\": \"更替小帮手的能量消耗与耐久\",\n  \"config.buildinggadgets.general.subCategoryGadgets.gadgetDestruction\": \"破坏小帮手\",\n  \"config.buildinggadgets.general.subCategoryGadgets.gadgetDestruction.tooltip\": \"破坏小帮手的能量消耗、耐久和最大能量\",\n  \"config.buildinggadgets.general.subCategoryGadgets.gadgetDestruction.maxEnergy\": \"最大能量\",\n  \"config.buildinggadgets.general.subCategoryGadgets.gadgetDestruction.maxEnergy.tooltip\": \"破坏小帮手的最大能量\",\n  \"config.buildinggadgets.general.subCategoryGadgets.gadgetDestruction.nonfuzzy.multiplier\": \"非模糊模式系数\",\n  \"config.buildinggadgets.general.subCategoryGadgets.gadgetDestruction.nonfuzzy.multiplier.tooltip\": \"当不处于模糊模式时，能量/耐久消耗将提高\",\n  \"config.buildinggadgets.general.subCategoryGadgets.gadgetDestruction.nonfuzzy.enabled\": \"非模糊模式已启用\",\n  \"config.buildinggadgets.general.subCategoryGadgets.gadgetDestruction.nonfuzzy.enabled.tooltip\": \"如果启用，则破坏小帮手可以脱离模糊模式，允许删除指定单击的方块（更高的耐久消耗）\",\n  \"config.buildinggadgets.general.subCategoryGadgets.gadgetCopyPaste\": \"复制粘贴小帮手\",\n  \"config.buildinggadgets.general.subCategoryGadgets.gadgetCopyPaste.tooltip\": \"复制粘贴小帮手的能量消耗与耐久\",\n  \"config.buildinggadgets.general.subCategoryPasteContainers\": \"构建黏膏容器\",\n  \"config.buildinggadgets.general.subCategoryPasteContainers.tooltip\": \"在此处配置构建黏膏容器\",\n  \"config.buildinggadgets.general.subCategoryPasteContainers.capacity.t1\": \"T1黏膏容器容量\",\n  \"config.buildinggadgets.general.subCategoryPasteContainers.capacity.t1.tooltip\": \"一级（铁）黏膏容器的最大容量\",\n  \"config.buildinggadgets.general.subCategoryPasteContainers.capacity.t2\": \"T2黏膏容器容量\",\n  \"config.buildinggadgets.general.subCategoryPasteContainers.capacity.t2.tooltip\": \"二级（金）黏膏容器的最大容量\",\n  \"config.buildinggadgets.general.subCategoryPasteContainers.capacity.t3\": \"T3黏膏容器容量\",\n  \"config.buildinggadgets.general.subCategoryPasteContainers.capacity.t3.tooltip\": \"三级（钻石）黏膏容器的最大容量\"\n}\n"
  },
  {
    "path": "src/main/resources/assets/buildinggadgets/lang/zh_tw.json",
    "content": "{\n  \"item.buildinggadgets.buildingtool\": \"建築小幫手\",\n  \"item.buildinggadgets.exchangertool\": \"更替小幫手\",\n  \"item.buildinggadgets.destructiontool\": \"破壞小幫手\",\n  \"block.buildinggadgets.effectblock\": \"（未使用）特效方塊\",\n  \"key.categories.buildingGadgets\": \"建築小幫手\",\n  \"key.anchorKey\": \"錨定\",\n  \"key.modeSwitch\": \"模式切換\",\n  \"key.rangeChange\": \"更改範圍\",\n  \"key.undoKey\": \"撤銷/模糊模式\",\n  \"tooltip.gadget.block\": \"方塊\",\n  \"tooltip.gadget.mode\": \"模式\",\n  \"tooltip.gadget.range\": \"範圍\",\n  \"tooltip.gadget.energy\": \"能量\",\n  \"message.gadget.invalidblock\": \"無效方塊\",\n  \"message.gadget.toolmode\": \"工具當前模式\",\n  \"message.gadget.toolrange\": \"工具當前範圍\",\n  \"message.gadget.anchorrender\": \"錨定點已渲染\",\n  \"message.gadget.anchorremove\": \"錨定點已移除\",\n  \"message.gadget.nothingtoundo\": \"沒有需要撤銷的操作\",\n  \"message.gadget.undofailed\": \"撤銷失敗，嘗試靠近一點？\",\n  \"message.gadget.fuzzymode\": \"模糊模式\",\n  \"message.gadget.copied\": \"區域已複製\",\n  \"item.buildinggadgets.constructionpastecontainer\": \"粘貼容器\",\n  \"item.buildinggadgets.constructionpastecontainert2\": \"粘貼容器 T2\",\n  \"item.buildinggadgets.constructionpastecontainert3\": \"粘貼容器 T3\",\n  \"item.buildinggadgets.constructionpaste\": \"構建糨糊\",\n  \"tooltip.pasteContainer.amount\": \"數量\",\n  \"block.buildinggadgets.constructionblock\": \"構建方塊\",\n  \"block.buildinggadgets.constructionblockpowder\": \"構建方塊粉末\",\n  \"block.buildinggadgets.templatemanager\": \"範本管理器\",\n  \"tooltip.constructionblockpowder.helptext\": \"放置在水旁\",\n  \"item.buildinggadgets.copypastetool\": \"複製-粘貼小幫手\",\n  \"message.gadget.rotated\": \"方塊已旋轉\",\n  \"message.gadget.toomanyblocks\": \"方塊過多，最大只允許 32,768 個（右擊空氣進行重置）\",\n  \"message.gadget.toobigarea\": \"區域過大，最大區域為 125x125x125（右擊空氣方塊進行重置）\",\n  \"message.gadget.areareset\": \"複製區域重置\",\n  \"tooltip.template\": \"名稱\",\n  \"item.buildinggadgets.template\": \"範本\",\n  \"message.gadget.pastetoobig\": \"粘貼過大\",\n  \"message.gadget.pastefailed\": \"粘貼失敗，非法的 JSON\",\n  \"message.gadget.copyfailed\": \"複製失敗，工具沒有數據\",\n  \"message.gadget.copyguierror\": \"複製區域失敗\",\n  \"message.gadget.copysuccess\": \"成功複製到剪貼板\",\n  \"message.gadget.pastesuccess\": \"從剪貼板粘貼成功\",\n  \"message.gadget.destroysizeerror\": \"最大破壞大小是 16x16x16，負數為非法數據\",\n  \"tooltip.gadget.destroyshowoverlay\": \"顯示覆蓋\",\n  \"tooltip.gadget.destroywarning\": \"警告：這個工具會直接銷毀方塊。\",\n  \"help.gui.buildinggadgets.button.help.enter\": \"顯示懸浮幫助文本\",\n  \"help.gui.buildinggadgets.button.help.exit\": \"退出幫助模式\",\n  \"help.gui.buildinggadgets.button.save\": \"將小幫手/範本的數據儲存到一個已有的範本裏，或者儲存到紙上來創建一個範本\",\n  \"help.gui.buildinggadgets.button.load\": \"從範本將數據加載到小幫手/範本\",\n  \"help.gui.buildinggadgets.button.copy\": \"將小幫手/範本的數據複製到電腦的剪貼板裏\",\n  \"help.gui.buildinggadgets.button.paste\": \"將電腦剪貼板上的數據粘貼到預先存在的範本或一張紙上，創建範本\",\n  \"help.gui.buildinggadgets.area.field.template_name\": \"保存/粘貼數據時，生成的範本將具有此名稱\",\n  \"help.gui.buildinggadgets.area.slot.gadget\": \"接受§n複製-粘貼小幫手§r或§n範本§r作為保存/複製的源和加載的目標\\\\n\\\\n§9覆蓋顏色§r指示接受數據的物品\",\n  \"help.gui.buildinggadgets.area.slot.template\": \"接受§n複製-粘貼小幫手§r或§n範本§r作為加載的源或保存/複製的目標\\\\n\\\\n§9覆蓋顏色§r指示接受數據的物品\",\n  \"help.gui.buildinggadgets.area.arrow.data_flow\": \"表示從一個物品到另一個物品的數據流；從範本到小幫手/範本，或從小幫手/範本到範本或一張紙\",\n  \"help.gui.buildinggadgets.area.preview\": \"預覽存儲在小幫手/範本中的數據\"\n}"
  },
  {
    "path": "src/main/resources/assets/buildinggadgets/models/block/blank_const_block.json",
    "content": "{\n  \"parent\": \"block/cube_all\",\n  \"textures\": {\n    \"all\": \"buildinggadgets:block/construction_block\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/buildinggadgets/models/block/construction_block.json",
    "content": "{\n  \"parent\": \"block/cube_all\",\n  \"textures\": {\n    \"all\": \"buildinggadgets:block/construction_block\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/buildinggadgets/models/block/construction_block_dense.json",
    "content": "{\n  \"parent\": \"block/cube_all\",\n  \"textures\": {\n    \"all\": \"buildinggadgets:block/construction_block_dense\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/buildinggadgets/models/block/construction_block_powder.json",
    "content": "{\n  \"parent\": \"block/cube_all\",\n  \"textures\": {\n    \"all\": \"buildinggadgets:block/construction_block_powder\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/buildinggadgets/models/block/constructionblock_dense.json",
    "content": "{\n  \"parent\": \"block/cube_all\",\n  \"textures\": {\n    \"all\": \"buildinggadgets:block/constructionblock_dense\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/buildinggadgets/models/block/effect_block.json",
    "content": "{\n  \"parent\": \"block/cube_all\",\n  \"textures\": {\n    \"all\": \"buildinggadgets:block/effect_block\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/buildinggadgets/models/block/orientable.json",
    "content": "{\n    \"parent\": \"block/cube\",\n    \"display\": {\n        \"firstperson_righthand\": {\n            \"rotation\": [ 0, 135, 0 ],\n            \"translation\": [ 0, 0, 0 ],\n            \"scale\": [ 0.40, 0.40, 0.40 ]\n        }\n    },\n    \"textures\": {\n        \"particle\": \"#front\",\n        \"down\": \"#bottom\",\n        \"up\": \"#top\",\n        \"north\": \"#front\",\n        \"east\": \"#side\",\n        \"south\": \"#side\",\n        \"west\": \"#side\"\n    }\n}"
  },
  {
    "path": "src/main/resources/assets/buildinggadgets/models/block/template_manager.json",
    "content": "{\n  \"parent\": \"buildinggadgets:block/orientable\",\n  \"textures\": {\n    \"particle\": \"buildinggadgets:block/template_manager_side\",\n    \"front\": \"buildinggadgets:block/template_manager_front\",\n    \"top\": \"buildinggadgets:block/template_manager_top\",\n    \"bottom\": \"buildinggadgets:block/template_manager_bottom\",\n    \"side\": \"buildinggadgets:block/template_manager_side\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/buildinggadgets/models/item/construction_block.json",
    "content": "{\n  \"parent\": \"buildinggadgets:block/construction_block\"\n}"
  },
  {
    "path": "src/main/resources/assets/buildinggadgets/models/item/construction_block_dense.json",
    "content": "{\n  \"parent\": \"buildinggadgets:block/construction_block_dense\"\n}"
  },
  {
    "path": "src/main/resources/assets/buildinggadgets/models/item/construction_block_powder.json",
    "content": "{\n  \"parent\": \"buildinggadgets:block/construction_block_powder\"\n}"
  },
  {
    "path": "src/main/resources/assets/buildinggadgets/models/item/construction_chunk_dense.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"buildinggadgets:item/construction_chunk_dense\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/buildinggadgets/models/item/construction_paste.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"buildinggadgets:item/construction_paste\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/buildinggadgets/models/item/construction_paste_container_creative.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"buildinggadgets:item/construction_paste_container_creative\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/buildinggadgets/models/item/construction_paste_container_t1.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"buildinggadgets:item/construction_paste_container_t1\"\n  },\n  \"overrides\": [\n    { \"predicate\": { \"level\": 0.0 }, \"model\": \"buildinggadgets:item/construction_paste_container_t1\" },\n    { \"predicate\": { \"level\": 0.25 }, \"model\": \"buildinggadgets:item/construction_paste_container_t1_quarter\" },\n    { \"predicate\": { \"level\": 0.5 }, \"model\": \"buildinggadgets:item/construction_paste_container_t1_half\" },\n    { \"predicate\": { \"level\": 0.75 }, \"model\": \"buildinggadgets:item/construction_paste_container_t1_3quarter\" },\n    { \"predicate\": { \"level\": 1.0 }, \"model\": \"buildinggadgets:item/construction_paste_container_t1_full\" }\n  ]\n}\n"
  },
  {
    "path": "src/main/resources/assets/buildinggadgets/models/item/construction_paste_container_t1_3quarter.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"buildinggadgets:item/construction_paste_container_t1_3quarter\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/buildinggadgets/models/item/construction_paste_container_t1_full.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"buildinggadgets:item/construction_paste_container_t1_full\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/buildinggadgets/models/item/construction_paste_container_t1_half.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"buildinggadgets:item/construction_paste_container_t1_half\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/buildinggadgets/models/item/construction_paste_container_t1_quarter.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"buildinggadgets:item/construction_paste_container_t1_quarter\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/buildinggadgets/models/item/construction_paste_container_t2.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"buildinggadgets:item/construction_paste_container_t2\"\n  },\n  \"overrides\": [\n    { \"predicate\": { \"level\": 0.0 }, \"model\": \"buildinggadgets:item/construction_paste_container_t2\" },\n    { \"predicate\": { \"level\": 0.25 }, \"model\": \"buildinggadgets:item/construction_paste_container_t2_quarter\" },\n    { \"predicate\": { \"level\": 0.5 }, \"model\": \"buildinggadgets:item/construction_paste_container_t2_half\" },\n    { \"predicate\": { \"level\": 0.75 }, \"model\": \"buildinggadgets:item/construction_paste_container_t2_3quarter\" },\n    { \"predicate\": { \"level\": 1.0 }, \"model\": \"buildinggadgets:item/construction_paste_container_t2_full\" }\n  ]\n}\n"
  },
  {
    "path": "src/main/resources/assets/buildinggadgets/models/item/construction_paste_container_t2_3quarter.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"buildinggadgets:item/construction_paste_container_t2_3quarter\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/buildinggadgets/models/item/construction_paste_container_t2_full.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"buildinggadgets:item/construction_paste_container_t2_full\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/buildinggadgets/models/item/construction_paste_container_t2_half.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"buildinggadgets:item/construction_paste_container_t2_half\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/buildinggadgets/models/item/construction_paste_container_t2_quarter.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"buildinggadgets:item/construction_paste_container_t2_quarter\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/buildinggadgets/models/item/construction_paste_container_t3.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"buildinggadgets:item/construction_paste_container_t3\"\n  },\n  \"overrides\": [\n    { \"predicate\": { \"level\": 0.0 }, \"model\": \"buildinggadgets:item/construction_paste_container_t3\" },\n    { \"predicate\": { \"level\": 0.25 }, \"model\": \"buildinggadgets:item/construction_paste_container_t3_quarter\" },\n    { \"predicate\": { \"level\": 0.5 }, \"model\": \"buildinggadgets:item/construction_paste_container_t3_half\" },\n    { \"predicate\": { \"level\": 0.75 }, \"model\": \"buildinggadgets:item/construction_paste_container_t3_3quarter\" },\n    { \"predicate\": { \"level\": 1.0 }, \"model\": \"buildinggadgets:item/construction_paste_container_t3_full\" }\n  ]\n}\n"
  },
  {
    "path": "src/main/resources/assets/buildinggadgets/models/item/construction_paste_container_t3_3quarter.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"buildinggadgets:item/construction_paste_container_t3_3quarter\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/buildinggadgets/models/item/construction_paste_container_t3_full.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"buildinggadgets:item/construction_paste_container_t3_full\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/buildinggadgets/models/item/construction_paste_container_t3_half.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"buildinggadgets:item/construction_paste_container_t3_half\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/buildinggadgets/models/item/construction_paste_container_t3_quarter.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"buildinggadgets:item/construction_paste_container_t3_quarter\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/buildinggadgets/models/item/effect_block.json",
    "content": "{\n  \"parent\": \"buildinggadgets:block/effect_block\"\n}"
  },
  {
    "path": "src/main/resources/assets/buildinggadgets/models/item/gadget_building.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"buildinggadgets:item/gadget_building\"\n  },\n  \"display\": {\n    \"thirdperson_righthand\": {\n      \"rotation\": [0, 0, 0 ],\n      \"translation\": [0, 0, 0 ],\n      \"scale\": [0.5,0.5,0.5]\n    },\n    \"firstperson_righthand\": {\n      \"rotation\": [-45, 0, 0 ],\n      \"translation\": [6, 0, -7 ],\n      \"scale\": [0.5,0.5,0.5]\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/buildinggadgets/models/item/gadget_copy_paste.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"buildinggadgets:item/gadget_copy_paste\"\n  },\n  \"display\": {\n    \"thirdperson_righthand\": {\n      \"rotation\": [0, 0, 0 ],\n      \"translation\": [0, 0, 0 ],\n      \"scale\": [0.5,0.5,0.5]\n    },\n    \"firstperson_righthand\": {\n      \"rotation\": [-45, 0, 0 ],\n      \"translation\": [6, 0, -7 ],\n      \"scale\": [0.5,0.5,0.5]\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/buildinggadgets/models/item/gadget_destruction.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"buildinggadgets:item/gadget_destruction\"\n  },\n  \"display\": {\n    \"thirdperson_righthand\": {\n      \"rotation\": [\n        0,\n        0,\n        0\n      ],\n      \"translation\": [\n        0,\n        0,\n        0\n      ],\n      \"scale\": [\n        0.5,\n        0.5,\n        0.5\n      ]\n    },\n    \"firstperson_righthand\": {\n      \"rotation\": [\n        -45,\n        0,\n        0\n      ],\n      \"translation\": [\n        6,\n        0,\n        -7\n      ],\n      \"scale\": [\n        0.5,\n        0.5,\n        0.5\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/buildinggadgets/models/item/gadget_exchanging.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"buildinggadgets:item/gadget_exchanging\"\n  },\n  \"display\": {\n    \"thirdperson_righthand\": {\n      \"rotation\": [0, 0, 0 ],\n      \"translation\": [0, 0, 0 ],\n      \"scale\": [0.5,0.5,0.5]\n    },\n    \"firstperson_righthand\": {\n      \"rotation\": [-45, 0, 0 ],\n      \"translation\": [6, 0, -7 ],\n      \"scale\": [0.5,0.5,0.5]\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/buildinggadgets/models/item/template.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"buildinggadgets:item/template\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/buildinggadgets/models/item/template_manager.json",
    "content": "{\n  \"parent\": \"buildinggadgets:block/template_manager\"\n}"
  },
  {
    "path": "src/main/resources/assets/buildinggadgets/sounds.json",
    "content": "{\n   \"beep\": { \"sounds\": [{ \"name\": \"buildinggadgets:beep\" }]}\n}"
  },
  {
    "path": "src/main/resources/data/buildinggadgets/loot_tables/blocks/construction_block.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"name\": \"buildinggadgets:construction_block#1\",\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"buildinggadgets:construction_paste\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/buildinggadgets/loot_tables/blocks/construction_block_dense.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"name\": \"buildinggadgets:construction_block_dense#1\",\n      \"entries\": [\n        {\n          \"type\": \"minecraft:alternatives\",\n          \"children\": [\n            {\n              \"type\": \"minecraft:item\",\n              \"conditions\": [\n                {\n                  \"condition\": \"minecraft:match_tool\",\n                  \"predicate\": {\n                    \"enchantments\": [\n                      {\n                        \"enchantment\": \"minecraft:silk_touch\",\n                        \"levels\": {\n                          \"min\": 1\n                        }\n                      }\n                    ]\n                  }\n                }\n              ],\n              \"name\": \"buildinggadgets:construction_block_dense\"\n            },\n            {\n              \"type\": \"minecraft:item\",\n              \"functions\": [\n                {\n                  \"function\": \"minecraft:set_count\",\n                  \"count\": {\n                    \"min\": 1.0,\n                    \"max\": 4.0,\n                    \"type\": \"minecraft:uniform\"\n                  }\n                },\n                {\n                  \"function\": \"minecraft:apply_bonus\",\n                  \"enchantment\": \"minecraft:fortune\",\n                  \"parameters\": {\n                    \"extra\": 1,\n                    \"probability\": 0.4\n                  },\n                  \"formula\": \"minecraft:binomial_with_bonus_count\"\n                }\n              ],\n              \"name\": \"buildinggadgets:construction_paste\"\n            }\n          ]\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/buildinggadgets/loot_tables/blocks/construction_block_powder.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"name\" : \"buildinggadgets:construction_block_powder#1\",\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"buildinggadgets:construction_block_powder\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/buildinggadgets/loot_tables/blocks/template_manager.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"name\": \"buildinggadgets:template_manager#1\",\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"functions\": [\n            {\n              \"function\": \"minecraft:copy_name\",\n              \"source\": \"block_entity\"\n            }\n          ],\n          \"name\": \"buildinggadgets:template_manager\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/buildinggadgets/recipes/construction_paste_container.json",
    "content": "{\n  \"result\": {\n    \"item\": \"buildinggadgets:construction_paste_container_t1\"\n  },\n  \"pattern\": [\n    \"iii\",\n    \"iri\",\n    \"iii\"\n  ],\n  \"type\": \"minecraft:crafting_shaped\",\n  \"key\": {\n    \"i\": {\n      \"tag\": \"forge:ingots/iron\"\n    },\n    \"r\": {\n      \"item\": \"buildinggadgets:construction_paste\"\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/data/buildinggadgets/recipes/construction_paste_container_t2.json",
    "content": "{\n  \"result\": {\n    \"item\": \"buildinggadgets:construction_paste_container_t2\"\n  },\n  \"pattern\": [\n    \"cgc\",\n    \"ggg\",\n    \"cgc\"\n  ],\n  \"type\": \"buildinggadgets:construction_paste\",\n  \"key\": {\n    \"g\": {\n      \"tag\": \"forge:ingots/gold\"\n    },\n    \"c\": {\n      \"item\": \"buildinggadgets:construction_paste_container_t1\"\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/data/buildinggadgets/recipes/construction_paste_container_t3.json",
    "content": "{\n  \"result\": {\n    \"item\": \"buildinggadgets:construction_paste_container_t3\"\n  },\n  \"pattern\": [\n    \"cgc\",\n    \"ggg\",\n    \"cgc\"\n  ],\n  \"type\": \"buildinggadgets:construction_paste\",\n  \"key\": {\n    \"g\": {\n      \"tag\": \"forge:gems/diamond\"\n    },\n    \"c\": {\n      \"item\": \"buildinggadgets:construction_paste_container_t2\"\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/data/buildinggadgets/recipes/construction_paste_powder.json",
    "content": "{\n  \"result\": {\n    \"item\": \"buildinggadgets:construction_block_powder\"\n  },\n  \"type\": \"minecraft:crafting_shapeless\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:sand\"\n    },\n    {\n      \"tag\": \"forge:gems/lapis\"\n    },\n    {\n      \"item\": \"minecraft:clay_ball\"\n    }\n  ]\n}\n"
  },
  {
    "path": "src/main/resources/data/buildinggadgets/recipes/gadget_building.json",
    "content": "{\n  \"result\": {\n    \"item\": \"buildinggadgets:gadget_building\"\n  },\n  \"pattern\": [\n    \"iri\",\n    \"drd\",\n    \"ili\"\n  ],\n  \"type\": \"minecraft:crafting_shaped\",\n  \"key\": {\n    \"i\": {\n      \"tag\": \"forge:ingots/iron\"\n    },\n    \"r\": {\n      \"tag\": \"forge:dusts/redstone\"\n    },\n    \"l\": {\n      \"tag\": \"forge:gems/lapis\"\n    },\n    \"d\": {\n      \"tag\": \"forge:gems/diamond\"\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/data/buildinggadgets/recipes/gadget_copy_paste.json",
    "content": "{\n  \"result\": {\n    \"item\": \"buildinggadgets:gadget_copy_paste\"\n  },\n  \"pattern\": [\n    \"iri\",\n    \"ere\",\n    \"ili\"\n  ],\n  \"type\": \"minecraft:crafting_shaped\",\n  \"key\": {\n    \"i\": {\n      \"tag\": \"forge:ingots/iron\"\n    },\n    \"r\": {\n      \"tag\": \"forge:dusts/redstone\"\n    },\n    \"l\": {\n      \"tag\": \"forge:gems/lapis\"\n    },\n    \"e\": {\n      \"tag\": \"forge:gems/emerald\"\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/data/buildinggadgets/recipes/gadget_destruction.json",
    "content": "{\n  \"result\": {\n    \"item\": \"buildinggadgets:gadget_destruction\"\n  },\n  \"pattern\": [\n    \"iri\",\n    \"ere\",\n    \"ili\"\n  ],\n  \"type\": \"minecraft:crafting_shaped\",\n  \"key\": {\n    \"i\": {\n      \"tag\": \"forge:ingots/iron\"\n    },\n    \"r\": {\n      \"tag\": \"forge:dusts/redstone\"\n    },\n    \"l\": {\n      \"tag\": \"forge:gems/lapis\"\n    },\n    \"e\": {\n      \"item\": \"minecraft:ender_pearl\"\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/data/buildinggadgets/recipes/gadget_exchanging.json",
    "content": "{\n  \"result\": {\n    \"item\": \"buildinggadgets:gadget_exchanging\"\n  },\n  \"pattern\": [\n    \"iri\",\n    \"dld\",\n    \"ili\"\n  ],\n  \"type\": \"minecraft:crafting_shaped\",\n  \"key\": {\n    \"i\": {\n      \"tag\": \"forge:ingots/iron\"\n    },\n    \"r\": {\n      \"tag\": \"forge:dusts/redstone\"\n    },\n    \"l\": {\n      \"tag\": \"forge:gems/lapis\"\n    },\n    \"d\": {\n      \"tag\": \"forge:gems/diamond\"\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/data/buildinggadgets/recipes/template_manager.json",
    "content": "{\n  \"result\": {\n    \"item\": \"buildinggadgets:template_manager\"\n  },\n  \"pattern\": [\n    \"iri\",\n    \"ere\",\n    \"ili\"\n  ],\n  \"type\": \"minecraft:crafting_shaped\",\n  \"key\": {\n    \"i\": {\n      \"tag\": \"forge:ingots/gold\"\n    },\n    \"r\": {\n      \"tag\": \"forge:dusts/redstone\"\n    },\n    \"l\": {\n      \"tag\": \"forge:gems/lapis\"\n    },\n    \"e\": {\n      \"tag\": \"forge:gems/emerald\"\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/data/buildinggadgets/tags/blocks/blacklist/building.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"#buildinggadgets:blacklist/generic\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/buildinggadgets/tags/blocks/blacklist/copy_paste.json",
    "content": "{\n  \"replace\": false,\n  \"values\": []\n}"
  },
  {
    "path": "src/main/resources/data/buildinggadgets/tags/blocks/blacklist/destruction.json",
    "content": "{\n  \"replace\": false,\n  \"values\": []\n}"
  },
  {
    "path": "src/main/resources/data/buildinggadgets/tags/blocks/blacklist/exchanging.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"#buildinggadgets:blacklist/generic\",\n    \"minecraft:torch\",\n    \"minecraft:wall_torch\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/buildinggadgets/tags/blocks/blacklist/generic.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"#minecraft:doors\",\n    \"#minecraft:beds\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/buildinggadgets/tags/blocks/whitelist/building.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"#buildinggadgets:whitelist/generic\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/buildinggadgets/tags/blocks/whitelist/copy_paste.json",
    "content": "{\n  \"replace\": false,\n  \"values\": []\n}"
  },
  {
    "path": "src/main/resources/data/buildinggadgets/tags/blocks/whitelist/destruction.json",
    "content": "{\n  \"replace\": false,\n  \"values\": []\n}"
  },
  {
    "path": "src/main/resources/data/buildinggadgets/tags/blocks/whitelist/exchanging.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"#buildinggadgets:whitelist/generic\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/buildinggadgets/tags/blocks/whitelist/generic.json",
    "content": "{\n  \"replace\": false,\n  \"values\": []\n}"
  },
  {
    "path": "src/main/resources/data/buildinggadgets/tags/items/template_convertible.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"minecraft:paper\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/create/recipes/crushing/dense_construction_block.json",
    "content": "{\n  \"conditions\": [\n    {\n      \"type\": \"forge:mod_loaded\",\n      \"modid\": \"create\"\n    }\n  ],\n  \"type\": \"create:crushing\",\n  \"group\": \"minecraft:misc\",\n  \"ingredients\": [\n    {\n      \"item\": \"buildinggadgets:construction_block_dense\"\n    }\n  ],\n  \"results\": [\n    {\n      \"item\": \"buildinggadgets:construction_paste\",\n      \"count\": 4\n    },\n    {\n      \"item\": \"buildinggadgets:construction_paste\",\n      \"count\": 1,\n      \"chance\": 0.1\n    }\n  ],\n  \"processingTime\": 50\n}"
  },
  {
    "path": "src/main/resources/data/create/recipes/milling/dense_construction_block.json",
    "content": "{\n  \"conditions\": [\n    {\n      \"type\": \"forge:mod_loaded\",\n      \"modid\": \"create\"\n    }\n  ],\n  \"type\": \"create:milling\",\n  \"ingredients\": [\n    {\n      \"item\": \"buildinggadgets:construction_block_dense\"\n    }\n  ],\n  \"results\": [\n    {\n      \"item\": \"buildinggadgets:construction_paste\",\n      \"count\": 3\n    },\n    {\n      \"item\": \"buildinggadgets:construction_paste\",\n      \"count\": 1,\n      \"chance\": 0.1\n    }\n  ],\n  \"processingTime\": 50\n}"
  },
  {
    "path": "src/main/resources/data/create/recipes/washing/construction_paste.json",
    "content": "{\n  \"conditions\": [\n    {\n      \"type\": \"forge:mod_loaded\",\n      \"modid\": \"create\"\n    }\n  ],\n  \"type\": \"create:splashing\",\n  \"group\": \"minecraft:misc\",\n  \"ingredients\": [\n    {\n      \"item\": \"buildinggadgets:construction_block_powder\"\n    }\n  ],\n  \"results\": [\n    {\n      \"item\": \"buildinggadgets:construction_block_dense\",\n      \"count\": 1\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/pack.mcmeta",
    "content": "{\n    \"pack\": {\n        \"description\": \"buildinggadgets resources\",\n        \"pack_format\": 6,\n        \"_comment\": \"A pack_format of 6 requires json lang files. Note: we require v6 pack meta for all mods.\"\n    }\n}"
  },
  {
    "path": "update.json",
    "content": "{\n  \"homepage\": \"https://minecraft.curseforge.com/projects/building-gadgets\",\n  \"1.12.2\": {\n    \"2.8.2\": \"Changelog: https://github.com/Direwolf20-MC/BuildingGadgets/blob/master/CHANGELOG.md\",\n    \"2.8.1\": \"Changelog: https://github.com/Direwolf20-MC/BuildingGadgets/blob/master/CHANGELOG.md\",\n    \"2.8.0\": \"Changelog: https://github.com/Direwolf20-MC/BuildingGadgets/blob/master/CHANGELOG.md\",\n    \"2.7.4\": \"Changelog: https://github.com/Direwolf20-MC/BuildingGadgets/blob/master/CHANGELOG.md\",\n    \"2.7.3\": \"Changelog: https://github.com/Direwolf20-MC/BuildingGadgets/blob/master/CHANGELOG.md\",\n    \"2.7.2\": \"Changelog: https://github.com/Direwolf20-MC/BuildingGadgets/blob/master/CHANGELOG.md\",\n    \"2.7.1\": \"Changelog: https://github.com/Direwolf20-MC/BuildingGadgets/blob/master/CHANGELOG.md\",\n    \"2.7.0\": \"Changelog: https://github.com/Direwolf20-MC/BuildingGadgets/blob/master/CHANGELOG.md\",\n    \"2.6.8\": \"Changelog: https://github.com/Direwolf20-MC/BuildingGadgets/blob/master/CHANGELOG.md\",\n    \"2.6.7\": \"Changelog: https://github.com/Direwolf20-MC/BuildingGadgets/blob/master/CHANGELOG.md\",\n    \"2.6.6\": \"Changelog: https://github.com/Direwolf20-MC/BuildingGadgets/blob/master/CHANGELOG.md\",\n    \"2.6.5\": \"Changelog: https://github.com/Direwolf20-MC/BuildingGadgets/blob/master/CHANGELOG.md\",\n    \"2.6.4\": \"Changelog: https://github.com/Direwolf20-MC/BuildingGadgets/blob/master/CHANGELOG.md\",\n    \"2.6.3\": \"Changelog: https://github.com/Direwolf20-MC/BuildingGadgets/blob/master/CHANGELOG.md\",\n    \"2.6.2\": \"Changelog: https://github.com/Direwolf20-MC/BuildingGadgets/blob/master/CHANGELOG.md\",\n    \"2.6.1\": \"Changelog: https://github.com/Direwolf20-MC/BuildingGadgets/blob/master/CHANGELOG.md\",\n    \"2.6.0\": \"Changelog: https://github.com/Direwolf20-MC/BuildingGadgets/blob/master/CHANGELOG.md\",\n    \"2.5.0\": \"Changelog: https://github.com/Direwolf20-MC/BuildingGadgets/blob/master/CHANGELOG.md\",\n    \"2.4.6\": \"Changelog: https://github.com/Direwolf20-MC/BuildingGadgets/blob/master/CHANGELOG.md\"\n  },\n  \"1.14.4\": {\n    \"3.0.6\": \"Changelog: https://github.com/Direwolf20-MC/BuildingGadgets/blob/master/CHANGELOG.md\",\n    \"3.0.5\": \"Changelog: https://github.com/Direwolf20-MC/BuildingGadgets/blob/master/CHANGELOG.md\",\n    \"3.1.0b\": \"Changelog: https://github.com/Direwolf20-MC/BuildingGadgets/blob/master/CHANGELOG.md\"\n  },\n  \"1.15.2\": {\n    \"3.3.4\": \"Changelog: https://github.com/Direwolf20-MC/BuildingGadgets/blob/master/CHANGELOG.md\",\n    \"3.3.5\": \"Changelog: https://github.com/Direwolf20-MC/BuildingGadgets/blob/master/CHANGELOG.md\"\n  },\n  \"1.16.1\": {\n    \"3.5.1\": \"Changelog: https://github.com/Direwolf20-MC/BuildingGadgets/wiki/Changelog-1.16\"\n  },\n  \"1.16.2\": {\n    \"3.6.0\": \"Changelog: https://github.com/Direwolf20-MC/BuildingGadgets/wiki/Changelog-1.16\"\n  },\n  \"1.16.3\": {\n    \"3.7.0\": \"Changelog: https://github.com/Direwolf20-MC/BuildingGadgets/wiki/Changelog-1.16\",\n    \"3.7.1\": \"Changelog: https://github.com/Direwolf20-MC/BuildingGadgets/wiki/Changelog-1.16\",\n    \"3.7.2\": \"Changelog: https://github.com/Direwolf20-MC/BuildingGadgets/wiki/Changelog-1.16\"\n  },\n  \"1.16.4\": {\n    \"3.7.3\": \"Changelog: https://github.com/Direwolf20-MC/BuildingGadgets/wiki/Changelog-1.16\"\n  },\n  \"1.16.5\": {\n    \"3.8.0\": \"Changelog: https://github.com/Direwolf20-MC/BuildingGadgets/wiki/Changelog-1.16\",\n    \"3.8.1\": \"Changelog: https://github.com/Direwolf20-MC/BuildingGadgets/wiki/Changelog-1.16\",\n    \"3.8.2\": \"Changelog: https://github.com/Direwolf20-MC/BuildingGadgets/wiki/Changelog-1.16\"\n  },\n  \"1.17.1\": {\n    \"3.9.0\": \"Changelog: https://github.com/Direwolf20-MC/BuildingGadgets/wiki/\"\n  },\n  \"1.18\": {\n    \"3.10.0\": \"Changelog: https://github.com/Direwolf20-MC/BuildingGadgets/wiki/\",\n    \"3.10.1\": \"Changelog: https://github.com/Direwolf20-MC/BuildingGadgets/releases/tag/release%2F3.10.1\"\n  },\n  \"1.18.1\": {\n    \"3.10.1\": \"Changelog: https://github.com/Direwolf20-MC/BuildingGadgets/releases/tag/release%2F3.10.1\"\n  },\n  \"1.18.2\": {\n    \"3.13.0\": \"Changelog: https://github.com/Direwolf20-MC/BuildingGadgets/releases/tag/release%2F3.13.0\"\n  },\n  \"1.19\": {\n    \"3.14.0\": \"Changelog: https://github.com/Direwolf20-MC/BuildingGadgets/releases/tag/release%2F3.14.0\",\n    \"3.14.1\": \"Changelog: https://github.com/Direwolf20-MC/BuildingGadgets/releases/tag/release%2F3.14.1\",\n    \"3.14.2\": \"Changelog: https://github.com/Direwolf20-MC/BuildingGadgets/releases/tag/release%2F3.14.2\",\n    \"3.16.3\": \"Changelog: https://github.com/Direwolf20-MC/BuildingGadgets/releases/tag/release%2F3.16.3\"\n  },\n  \"promos\": {\n    \"1.12.2-latest\": \"2.8.2\",\n    \"1.12.2-recommended\": \"2.8.2\",\n    \"1.14.4-latest\": \"3.1.0b\",\n    \"1.14.4-recommended\": \"3.1.0b\",\n    \"1.15.2-latest\": \"3.3.4\",\n    \"1.15.2-recommended\": \"3.3.4\",\n    \"1.16.1-latest\": \"3.5.1\",\n    \"1.16.1-recommended\": \"3.5.1\",\n    \"1.16.2-latest\": \"3.6.0\",\n    \"1.16.2-recommended\": \"3.6.0\",\n    \"1.16.3-latest\": \"3.7.2\",\n    \"1.16.3-recommended\": \"3.7.2\",\n    \"1.16.4-latest\": \"3.7.3\",\n    \"1.16.4-recommended\": \"3.7.3\",\n    \"1.16.5-latest\": \"3.8.2\",\n    \"1.16.5-recommended\": \"3.8.2\",\n    \"1.17.1-latest\": \"3.9.0\",\n    \"1.17.1-recommended\": \"3.9.0\",\n    \"1.18-latest\": \"3.10.0\",\n    \"1.18-recommended\": \"3.10.0\",\n    \"1.18.1-latest\": \"3.10.1\",\n    \"1.18.1-recommended\": \"3.10.1\",\n    \"1.18.2-latest\": \"3.13.0\",\n    \"1.18.2-recommended\": \"3.13.0\",\n    \"1.19-latest\": \"3.16.3\",\n    \"1.19-recommended\": \"3.16.3\"\n  }\n}\n"
  }
]